Back to work

Lead operations · n8n build with live Slack and HubSpot

Lead-to-Booking Agent.

A web form, HubSpot form or tracked call becomes a qualified HubSpot deal with three proposed appointment slots, a task for the office and a Slack summary. Claude reads the lead, rules price it, and a manager approves the risky jobs from Slack.

n8n canvas of the Book new lead workflow in ten sections: signed webhook and event ledger, Claude qualification and guardrails, routing, existing-customer check, HubSpot contact, review and out-of-area paths, deal, availability and approval, outreach drafts and CSR handoff, and the final log and response
Actual local n8n canvas · 80 nodes in ten sectionsOpen for full-size detail
Role
Workflow architecture & implementation
Client
Portfolio system
Tools
n8n, Claude API, HubSpot API, Slack API, FieldRoutes API, ClickUp API, Gmail API, Google Sheets
Year
2026

Inside the automation · Three workflows · 36 end-to-end checks · 117 unit tests

Every lead is booked or handed to a person.

Built for a fictional pest control company with branches in Jacksonville, Savannah and Mobile. Slack and HubSpot ran in real test accounts; FieldRoutes, ClickUp, Gmail, Google Sheets and the event ledger are local mocks of their APIs, and Claude's answers are real recordings replayed in tests.

01 · Intake

Trust the request before anything runs

A website form, a HubSpot form and a call-tracking transcript all arrive at one webhook. The workflow checks an HMAC signature and a five-minute timestamp window, normalizes names, phones and ZIP codes, and claims the event in a ledger, so a repeat delivery is answered as a duplicate instead of booking twice.

  1. 01Verify
  2. 02Normalize
  3. 03Claim
  4. 04Route

02 · Qualify

Let Claude read it, then check the answer

Claude returns a fixed JSON shape: pest type, urgency, property type, service interest, confidence and a one-line summary. A guardrail step re-validates every field and strips phone numbers and emails the model echoes back. An invalid answer, a refusal, a failed call or confidence under 0.6 goes to a person, not a deal.

  1. 01Prompt
  2. 02Classify
  3. 03Validate
  4. 04Review

03 · Book

Price it, find slots, create the deal

Prices and approval rules come from an ops sheet, not the model. The workflow updates or creates the HubSpot contact, looks the deal up by a unique event id before creating one, pulls three open two-hour windows from FieldRoutes inside the urgency window, drafts the Gmail reply, and gives the office a ClickUp task with a callback deadline.

  1. 01Price
  2. 02Contact
  3. 03Deal
  4. 04Slots
  5. 05Task

04 · Approve

Hold the risky jobs for a manager

Termite and wildlife jobs, commercial properties and estimates of $800 or more post to Slack with Approve and Hold. A signed relay forwards the click, only listed approvers can decide, and the first decision wins. The HubSpot deal moves to Approved or On hold, and the Slack message records who decided and when.

  1. 01Post
  2. 02Click
  3. 03Verify
  4. 04Decide
  5. 05Update

05 · Recover

Fail loudly, retry safely

Every network call retries three times, five seconds apart, with an idempotency key, so a retry never writes twice. Steps that can fail without losing the lead have their own error branch: no slots becomes a callback task. When a required step still fails, an error workflow releases the event for redelivery and alerts #automation-alerts with the event id and no customer data.

  1. 01Retry
  2. 02Branch
  3. 03Release
  4. 04Alert

The problem

A field service office loses jobs between the inquiry and the callback. Leads arrive from the website, a HubSpot form and phone calls, and someone has to read each one. They have to work out the pest and the urgency, check whether the caller is already a customer, price the job, find a technician window and create the CRM record. Those steps are the same for every lead, and they happen while the customer is calling the next company on the list.

I built the Lead-to-Booking Agent as that one silo of a pest control back office. The company is fictional: Ridgeway Pest Control, with branches in Jacksonville, Savannah and Mobile. The rule I designed around is that every lead leaves the workflow in one of two states. Either it is ready to book, with a deal, proposed slots, a drafted reply and a task for the office, or it is explicitly handed to a person. It is never silently dropped.

How it is built

It is three n8n workflows:

The workflow JSON is generated from source files and kept in git. It is checked with n8n-mcp's validator, with zero errors, before it is imported. The logic inside the Code nodes is plain functions with 117 unit tests that run without n8n.

The n8n canvas from the ops settings to Claude: Fetch ops settings, Prepare Claude request, Qualify lead with Claude with success and error outputs, and Apply AI guardrails, under a note that says the model output is never trusted and that invalid output or low confidence goes to needs_review

Claude reads the lead and nothing else. The call asks for a JSON schema, and the answer is treated as untrusted input:

The model does not decide prices, approval thresholds, service areas or HubSpot stages. Those come from an ops rules sheet, so changing a price is a sheet edit, not a prompt change.

Every network call retries three times, five seconds apart. Every write carries an idempotency key built from the event id and the node name. HubSpot ignores idempotency keys, so each deal also carries a unique ridgeway_event_id property, and the workflow reads the deal by that id before it creates one.

Approval in Slack

A real Slack message from the ridgeway-lead-agent app in #leads: a new emergency lead for fire ants at an HOA's common grounds in Jacksonville, estimated at $1,155, with three proposed two-hour slots, the HubSpot contact and deal links, the reason "commercial_property, estimated_value>=800", and green Approve and Hold buttons

The workflow posted this message to a real Slack workspace I set up for the demo. The lead itself is from the simulated day. Commercial properties, termite and wildlife jobs, and estimates of $800 or more are held for a manager, and the message says which rule held it.

The Approve and Hold buttons come back through a small Socket Mode relay that signs each click. Only listed approvers can decide, anyone else gets a private notice, and the first decision wins: a second click or a Slack redelivery changes nothing.

The real HubSpot deal record for Yolanda Prescott, termites, Jacksonville: amount $1,200, pipeline Ridgeway Sales, deal stage Approved, with one associated contact

An Approve click in that workspace moved the deal in a HubSpot developer test account from Needs approval to Approved. The workflow then commented on the office's task and replaced the buttons with who approved it and when.

When things fail

The n8n error workflow, Pest Lead Agent: alert on errors, with its note and six nodes: Receive error, Describe failure, Release failed event, Compose alert, Post Slack automation alert, and Append error log row

The hardest bug was duplicates. A second delivery of the same lead, arriving while the first run was still going, could start its own booking, because n8n saves workflow static data only when a run finishes. I moved the duplicate check into an external event ledger:

Steps that can fail without losing the lead have their own error path. If FieldRoutes is down, the office gets a "call back to book", and if Gmail fails, the task says no draft was created. If a required step fails after all its retries, the error workflow posts the event id to #automation-alerts, with no customer data, and logs the error.

Verification

The end-to-end suite runs the real workflows on an n8n 2.42.6 server against local mocks of every vendor. There are 36 checks: 21 scenarios plus the approval and error-path checks. Failures are injected on purpose:

Each check asserts the response, every request the mock received and what exists in the mock afterwards.

A simulated 40-lead day ran through the same workflows. Of the 40 deliveries, 27 got proposed slots and 8 were held for approval. Two went to review, one existing customer became a service request, one lead was outside the service area, and the one duplicate was caught.

The same day was then run twice against HubSpot. The first run created 33 deals and reused 2, and the second created none and reused all 35.

With mocked vendors and replayed answers, the median time from webhook to office task was 0.25 seconds. That is the workflow's own overhead. The recorded Claude call itself took a median of 3.3 seconds.

The code, the build log and the full results are on GitHub.

What it does not show

This is a portfolio build. It has not run in production or touched a real customer:

For a real office I would make four changes:

Next

Trench Desk

Trench Desk