Standard Compute
Flat-rate, fixed monthly price
← Blog/Guide

My n8n lead agent looked fine until I realized it was dying at the Apollo, HubSpot, and JSON handoffs

Daniel Nguyen
Daniel NguyenAugust 30, 2026 · 6 min read
Lead Agent Handoffs
n8n
Duplicate enrich
Apollo
200 / empty
HubSpot
423 Locked
JSON
Bad retry loop
Failure Points
Apollo
200 empty
HubSpot
423
JSON
retry
Model JSON
{ "prospect": "...",
"email": null,
"status": "retry"
Retry loop

Most tool calling failures in an n8n lead-enrichment agent are not LLM reasoning problems. They happen between Apollo, HubSpot, and your JSON contracts: Apollo can return HTTP 200 with no enriched record, HubSpot can return 423 Locked for 2 seconds, and partial JSON quietly poisons retries and duplicate writes.

My n8n lead agent looked fine until I realized it was dying at the Apollo, HubSpot, and JSON handoffs

At 2:13 a.m. my n8n lead-enrichment flow marked the same prospect as enriched twice, Apollo returned HTTP 200 with nothing useful, HubSpot threw 423 Locked, and GPT-5 kept confidently retrying bad JSON.

The annoying part is that the workflow looked healthy at a glance. Green checks in n8n. No dramatic stack trace. Just a slow drip of duplicate contacts, half-filled records, and retries that made the whole thing more expensive without making it more correct.

This is not an LLM intelligence problem. It is a contract-design problem, and a lot of n8n builders are blaming GPT-5 for bugs caused by Apollo edge cases, HubSpot write behavior, and sloppy JSON validation. My strong opinion after debugging this: strict JSON contracts with idempotency keys beat “let the model fix it on retry” every time.

I originally thought the model was the weak link. That was the false diagnosis. The real failures were happening in the handoffs between n8n, Apollo’s enrichment API, HubSpot’s contact write API, and the model’s JSON output.

The symptom looked like flaky AI

The flow was simple on paper:

  1. Pull a new lead into n8n
  2. Send the lead to Apollo for enrichment
  3. Ask GPT-5 to normalize the result into the fields I wanted
  4. Write the contact into HubSpot
  5. Retry if anything failed

That sounds reasonable until step 2 returns something technically successful but operationally useless, step 3 turns partial input into confident-looking JSON, and step 4 hits a temporary lock right when your retry logic is least careful.

So the first symptom was not “the agent crashed.” The first symptom was worse: the agent kept running.

Why does Apollo return 200 when the lead is still unusable?

This was the first ugly surprise. Apollo can return HTTP 200 even when the enrichment result does not contain enough identity data to be useful for the next step.

If your n8n branch only checks the status code, the workflow moves on as if the lead is enriched. Then GPT-5 gets a payload with missing fields, tries to be helpful, and emits JSON that is structurally valid enough to pass a casual glance but semantically wrong for your HubSpot write.

That is where people say, “the model hallucinated.” Sometimes it did. But in my case the bigger problem was that I asked the model to normalize a record that Apollo never really enriched in the first place.

My rule now is simple: Apollo does not count as successful unless the payload contains the exact fields the next step requires. HTTP 200 is not success. A usable person or company record is success.

That one change removed a huge amount of fake progress from the workflow.

What should n8n do when HubSpot returns 423 Locked?

This was the second ugly surprise. HubSpot can return 423 Locked for a short window, often around 2 seconds. If your workflow treats that like a generic failure and immediately retries the same write, you can create the exact mess you were trying to avoid.

My bad version of this flow did three things wrong:

  • It retried too fast
  • It retried without a real idempotency key
  • It let the fallback path create a new contact instead of proving whether the first write eventually landed

That is how you get duplicate contacts and confusing audit trails.

The better pattern is boring, which is why it works:

  • Use email as the primary unique key when possible
  • Store an idempotency key in n8n before the HubSpot write
  • On 423 Locked, wait a little longer than your instinct says
  • Re-check HubSpot before retrying a create path
  • Separate “retry the same write” from “attempt a new create”

This is where I stopped blaming GPT-5 entirely. No model can rescue bad write semantics.

The false diagnosis was “just prompt it better”

My first fix was the classic one: tighten the prompt, ask for cleaner JSON, add more examples, and tell the model not to invent missing fields.

That helped a little. It did not solve the real problem.

Prompting harder is the wrong first move when Apollo is returning thin records and HubSpot is temporarily locking writes. You are polishing the middle of the pipeline while the ends are lying to each other.

If I had to pick one loser pattern, it is this: “let the model fix it on retry.” That pattern burns time, burns tokens, and hides the real bug. The winner is strict validation before and after every named service call.

The fix was less agentic than I wanted

I wanted a clever lead-enrichment agent. What actually worked was a more disciplined workflow.

The fixes were not glamorous:

  • Validate Apollo payloads against required fields before any model call
  • Use Structured Outputs instead of “please return valid JSON” whenever possible
  • Reject partial JSON instead of trying to salvage it downstream
  • Add idempotency keys before HubSpot writes
  • Treat HubSpot 423 Locked as a timed retry case, not a generic error
  • Log every payload transition between n8n, Apollo, GPT-5, and HubSpot

The most important change was deciding that partial success is failure. Once I stopped letting weak Apollo results and half-valid model output sneak through, the workflow got much quieter.

Why this gets expensive fast if you pay per token

This part matters for anyone running agents in n8n, Make, Zapier, OpenClaw, or custom automations at real volume.

When a workflow bounces across Apollo, HubSpot, and multiple model calls, debugging is not a single request. It is a chain of retries, schema checks, reformats, and replay runs. If you are paying per token, reliability work gets punished twice: once in development, and again in production when edge cases trigger extra model calls.

That pricing model changes behavior. Teams become conservative about testing. They avoid aggressive replay. They hesitate to add validation loops because every safeguard has a visible marginal cost.

Flat-rate compute changes that. If your API layer can route across models and absorb heavy retry and testing behavior without surprise bills, you can afford to build the safer version of the workflow instead of the cheapest-looking one. For teams running lead agents all day, predictable cost is not just a finance benefit. It changes how seriously you can treat reliability.

What should people building in n8n, Make, or Zapier stop doing?

Stop treating status-code success as workflow success.

Stop asking GPT-5 to paper over missing Apollo data.

Stop retrying HubSpot writes without idempotency.

Stop calling malformed or partial JSON “close enough.”

And stop assuming the expensive part of the workflow is the smart part. In my experience, the real damage happens in the boring places: the API response you did not validate, the lock you retried too quickly, and the duplicate write you did not make idempotent.

That was the lesson for me. My n8n lead agent did not need a smarter model nearly as much as it needed stricter contracts between Apollo, GPT-5, HubSpot, and the workflow itself.

If you are seeing “flaky AI” in lead enrichment, look there first.

Frequently Asked Questions

Why does my n8n lead enrichment agent fail silently?

A lot of silent failures happen at the boundaries between services, not inside the agent loop. Apollo can return HTTP 200 without actually enriching a record, HubSpot batch endpoints can partially fail, and prompt-only JSON formatting often produces outputs that look valid until a downstream step breaks.

Can Apollo return success even when no lead was enriched?

Yes. Apollo documents that weak identity data can still produce an HTTP 200 response while no record is enriched, which means a status-code check alone is not enough. You need to validate that the payload contains the fields your workflow actually requires.

How do I stop HubSpot retries from creating duplicate contacts?

Use email as the primary unique identifier and prefer upsert-style logic over blind create calls. HubSpot also documents 423 Locked as a transient error lasting 2 seconds, so immediate retries can make duplicate-write problems worse if your workflow falls back to create.

Are Structured Outputs better than telling the model to return valid JSON?

Yes, for this use case they are materially better. OpenAI Structured Outputs enforce a supplied JSON Schema, which reduces partial or malformed JSON and makes validation and refusals programmatically detectable.

Do I always need full validation and polling in a small enrichment workflow?

No. If you only enrich a small number of leads per day, a simpler deterministic chain may be easier to maintain than a full agent workflow with polling, idempotency, and error branches. The heavier safeguards matter most once retries, async enrichment, and CRM writes start happening at scale.

Ready to stop paying per token?One flat monthly price — no per-token fees, no surprise bills. Try the free tier first, no card needed.
Get started free

Keep reading