The harness: the same webhook twice does nothing twice
A reliable sales agent is a compiled graph: typed steps, fixed tools, one approval interrupt, idempotent writes, checkpoints, and a CRM login with hard limits.
Key takeaways
- A reliable AI sales agent is a compiled state graph with typed nodes, a fixed tool allow-list and one interrupt where a person approves before anything is written.
- Every write is keyed on the run, the tool and the target record, so a redelivered webhook re-runs the graph and lands on the same records instead of duplicating them.
- A checkpoint after each step lets a failed run resume from the last successful step without re-extracting the call or re-drafting mail already written.
- In Salesforce the boundary on stage, dates and amount is a field permission on the integration user; HubSpot's property restrictions do not apply to API writes, so there the write tool's allow-list carries it.
A reliable AI sales agent is a compiled graph, not a long prompt: typed steps, a fixed list of tools, one pause where a person decides, writes that are safe to repeat, a checkpoint after every step, and a CRM login that cannot touch stage, dates or amount. The interesting engineering is the boundary around the model, and knowing which steps should stop being agents at all. This part is for the head of sales whose CTO asks how it actually runs.
What is the harness connected to?
- Typed nodes, conditional edges
- A fixed tool allow-list
- One interrupt: a person decides
- A checkpoint after every node
- Writes keyed on run, tool and record
In the Lost Star build the graph is LangGraph and the model steps run through Claude Code. The names matter less than the shape. A compiled state graph means every step is a node with typed inputs and outputs, and the edges between them are conditions written in code: if an extraction comes back below its confidence threshold, the next node is the rep's review queue, not the CRM. The tool list is fixed when the graph is compiled. The node that extracts signals from a call cannot call the email tool, and the node that drafts the recap cannot call the CRM write.
What does "the same webhook twice does nothing twice" mean?
The run starts when the recorder says a recording is ready, usually through a webhook. Webhook senders retry when they do not get a quick answer, so the same event can arrive twice. Without protection, the Northwind call from part 1 produces eight open issues instead of four, two recap drafts, and two notes on the deal.
The fix is to make every write idempotent: keyed on the run, the tool and the target record. Before a write, the harness checks the key. If it already exists, it returns the earlier result instead of writing again. A redelivered event re-runs the graph and lands on the same four issues in the open-issue ledger, not eight. Reps stop trusting a ledger the first time it doubles.
What happens when a run fails halfway?
It picks up where it stopped. LangGraph's checkpointer saves a snapshot of the graph's state at each step, and its documentation describes the recovery directly: if a node fails, you restart from the last successful step, and writes from nodes that already finished in that step are not run again.
In practice, a timeout on the price book call does not re-extract the recording or re-draft the recap that is already written. That matters for two reasons. The model steps are the slow and expensive part, so re-running them because one API blinked is waste. And a re-drafted recap can come out different from the one the rep already read, which is worse than waste.
Where does a person decide?
At one interrupt. LangGraph's interrupts pause the graph at a chosen point, save its state, and wait, indefinitely if need be, until the run is resumed with outside input. In this harness the interrupt sits at the approval step. The drafted record waits in review. When the rep approves, edits or rejects each field, the graph resumes with that decision and only then writes.
That is the difference between a promise and a structure. A sentence in a prompt asking the model to wait for approval can be ignored. A node the graph cannot route around cannot.
How is the boundary enforced, not promised?
Part 1 said the agent's CRM login cannot write stage, dates or amount. How that is enforced depends on the CRM, and the difference is worth knowing before you sign anything.
In Salesforce it is a permission. Field permissions control whether a user can see and edit a particular field. The agent signs in as its own integration user, and that user's permission set denies edit on stage, close date and amount. The runtime could not move them if the prompt told it to.
In HubSpot it has to live one layer up. HubSpot's property restrictions are an Enterprise feature, and HubSpot's own documentation says users can still set restricted properties through the API and that restrictions should not be used as a security measure. So on HubSpot the write tool carries the boundary: it accepts an allow-list of evidence properties, has no parameter for deal stage, close date or amount, rejects anything else before the API call, and logs every write with its run. That is code, not a CRM permission, which is why the log is how you audit it.
Which steps should stop being agents?
An agent is how you find the shape of a job. It is an expensive way to keep running one. The same seven steps, today and at fifty times the volume:
| Step | Today | At 50x volume | Why |
|---|---|---|---|
| Pull signals from a call | Agent | Agent | Language is unbounded. No fixed schema survives contact with real calls. |
| Draft the recap and follow-ups | Agent | Agent | It has to sound like the rep wrote it. |
| Classify an inbound question | Agent | Agent | New questions arrive forever. A rules engine rots. |
| Write evidence to the CRM | Agent tool call | Deterministic job | Once the field map settles, it is an upsert. |
| Create open issues | Agent tool call | Deterministic job | The typing already happened upstream. |
| Build the quote | Agent tool call | Price book function | It is arithmetic against a price book, not judgment. |
| Detect a deal gone quiet | Agent | A scheduled query | Reply timestamps and a threshold. No model needed. |
A step graduates the moment its output schema stops changing. The agent is how you find that schema, not how you run it forever. Four account executives might record two hundred calls a month, an illustrative figure; fifty times that is ten thousand. At ten thousand calls a month, paying a model to ask whether a deal has gone quiet is a bill, not an architecture. The first three rows are language problems and stay agents. The last four are schema problems wearing a language problem's clothes.
The outbound series draws the same line from the other side: automate the known, escalate the consequential. A known event with an approved action belongs in a job. An ambiguous one belongs in front of a person.
Next in the series
Part 12, What to measure, and what the published numbers already say, covers how you would know any of this worked: the industry figures worth quoting with their weaknesses, two worth refusing, and the measures to baseline before anything ships.
Common questions
- What makes an AI agent safe to run on a real CRM?
- Structure rather than instructions: a compiled graph with a fixed tool list, an approval step the graph cannot skip, idempotent writes so repeated events do not duplicate records, checkpoints so failed runs resume cleanly, and a CRM login or write tool that cannot touch stage, close date or amount.
- Can HubSpot field permissions stop an AI agent from changing deal stage or amount?
- Not on their own. HubSpot's documentation says restricted properties can still be set through the API and advises against using property restrictions as a security measure. On HubSpot the limit has to be enforced in the agent's write tool, with an allow-list of properties and a log of every write. In Salesforce, field permissions on the integration user enforce it directly.
- Should every step in a sales workflow be an AI agent?
- No. Steps that read or write open-ended language, like extracting signals from a call or drafting a recap, stay agents. Steps whose output schema has stopped changing, like writing approved fields, creating issues, pricing a quote or flagging a quiet deal, should become deterministic jobs as volume grows.
Sources
- LangChain, LangGraph documentation: Checkpointers (checkpoints at each step, fault tolerance, pending writes)
- LangChain, LangGraph documentation: Interrupts (pause, persist state, resume with input)
- Salesforce Trailhead, Data Security module, unit on field-level security (field permissions control whether a user can see and edit a field)
- HubSpot Knowledge Base, Restrict view and edit access for properties (API edits not restricted)
Written by Jared Hawkins-Young from the system he builds and runs. Diagrams are drawn from the same specs as the software. Published October 8, 2026. Illustrative numbers are labelled as such; every other figure carries its source.