Cleaning Messy Text into Structured JSON in n8n with an LLM

One thing I keep running into when building n8n workflows is that the hard part is not always the automation itself.

A lot of the time, the annoying part is the input.

Customer messages are messy. Form submissions are inconsistent. Emails contain signatures, greetings, random formatting, missing fields, and sometimes five different things in one paragraph.

Before the workflow can actually do anything useful, the data needs to be cleaned and shaped into something predictable.

So in this post, I want to share a simple pattern I use in n8n: use an LLM to turn messy text into structured JSON, then let the rest of the workflow handle routing, storage, notifications, or CRM updates.

Disclosure: I am on the TokenBay team, so I’ll use TokenBay in the example below. The workflow pattern itself should work with any OpenAI-compatible endpoint.

The problem

Let’s say you receive a customer message like this from a form, email, or webhook:

text

Hi, this is Daniel from Northstar Studio. We’re looking for an AI chatbot for our website.
Probably around 3 seats to start. Can someone contact me this week?
My email is daniel@northstarstudio.com. Budget is not confirmed yet.

As a human, this is easy to understand.

But for an automation workflow, this is not very convenient.

You may want to extract:

json

{
"name": "Daniel",
"company": "Northstar Studio",
"email": "daniel@northstarstudio.com",
"use_case": "AI chatbot for website",
"team_size": 3,
"urgency": "this_week",
"budget_status": "not_confirmed"
}

Once the data looks like this, n8n can do much more with it:

  • send high-intent leads to Slack
  • create a CRM record
  • assign the lead to sales
  • store the cleaned data in Airtable or Google Sheets
  • trigger different follow-up emails based on urgency

The LLM is not replacing the workflow logic here. It is just doing the annoying extraction step.

Basic workflow idea

The workflow can be very simple:

  1. Receive messy text from a webhook, form, email, or CRM note.
  2. Send the text to an LLM through an HTTP Request node.
  3. Ask the model to return strict JSON.
  4. Parse the JSON in n8n.
  5. Use IF / Switch nodes to route the result.

This keeps the workflow clean because the messy language processing happens in one place.

HTTP Request node setup

Create an HTTP Request node in n8n.

Use the following setup:

  • Method: POST
  • URL: https://api.tokenbay.com/v1/chat/completions
  • Authentication: Header Auth or custom headers
  • Header: Authorization: Bearer YOUR_TOKENBAY_API_KEY
  • Header: Content-Type: application/json

Example JSON body:

json

{
"model": "gpt-5.4-mini",
"messages": [
{
"role": "system",
"content": "You extract structured data from messy text. Return valid JSON only. Do not include markdown, explanations, or extra text."
},
{
"role": "user",
"content": "Extract the following fields from this message: name, company, email, use_case, team_size, urgency, budget_status. If a field is missing, return null. Message: {{$json.message}}"
}
],
"temperature": 0
}

I usually keep temperature low for this kind of task. This is not creative writing. The goal is consistency.

Example output

For the input message above, the model should return something like:

json

{
"name": "Daniel",
"company": "Northstar Studio",
"email": "daniel@northstarstudio.com",
"use_case": "AI chatbot for website",
"team_size": 3,
"urgency": "this_week",
"budget_status": "not_confirmed"
}

Now the workflow can use the output like normal structured data.

For example:

  • If urgency is this_week, send a Slack notification.
  • If email is not null, create a CRM contact.
  • If team_size is greater than 2, mark the lead as qualified.
  • If budget_status is not_confirmed, send a softer follow-up email.

This is where n8n becomes useful. The LLM handles the language mess, and n8n handles the actual business logic.

Make the JSON schema stricter

One mistake I see often is giving the model a vague instruction like:

text

Extract useful information from this message.

That usually works in a demo, but it becomes unreliable in a real workflow.

A better prompt is to define the exact JSON shape you want:

json

{
"model": "gpt-5.4-mini",
"messages": [
{
"role": "system",
"content": "You are a data extraction engine. Return valid JSON only."
},
{
"role": "user",
"content": "Extract data from this message and return exactly this JSON structure: {\"name\": string or null, \"company\": string or null, \"email\": string or null, \"use_case\": string or null, \"team_size\": number or null, \"urgency\": \"today\" | \"this_week\" | \"later\" | null, \"budget_status\": \"confirmed\" | \"not_confirmed\" | \"unknown\"}. Message: {{$json.message}}"
}
],
"temperature": 0
}

This makes downstream parsing much easier.

If the workflow expects a field called budget_status, you do not want the model randomly returning budget, budgetInfo, or payment_status.

Tiny naming differences can break later nodes. Boring consistency is the goal.

Add a validation step

I would not send the model output directly into important actions without checking it first.

After the HTTP Request node, add a validation step using an IF node or Code node.

At minimum, check:

  • Is the response valid JSON?
  • Does it contain the required fields?
  • Is the email actually present?
  • Is the urgency value one of the allowed values?
  • Are important fields null?

For example, if email is missing, the workflow can send the item to a manual review path instead of creating a broken CRM contact.

This is a very n8n-friendly approach: let the LLM extract, but let the workflow decide.

Why this pattern is useful

This setup is useful when your input is too messy for simple rules, but you still need structured output.

Some practical examples:

  • cleaning inbound leads
  • extracting support ticket details
  • parsing partnership requests
  • turning long emails into CRM fields
  • classifying job applications
  • extracting invoice or order notes
  • preparing content briefs from messy form submissions

The key is to use the LLM for the part it is good at: understanding unstructured language.

Then use n8n for what it is good at: moving data, checking conditions, updating tools, and triggering actions.

Where TokenBay fits

For this example, I used TokenBay:

The homepage currently lists 500 free credits, 15% off most models, and referral credits, so it is enough to test a small extraction workflow before committing real usage.

For this type of JSON extraction task, I would start with a cheaper model first and only move to a stronger model if the extraction quality is not good enough.

Final thought

For me, this is one of the most practical ways to use LLMs in n8n.

Not every workflow needs an AI agent. A lot of the time, you just need a reliable step that turns messy human text into clean structured data.

Once the data is clean, the rest of the automation becomes much easier.

Would love to hear how others are handling messy text extraction in n8n workflows.

1 Like

Hey Gwen, fantastic breakdown. You’ve hit the nail on the head—handling unstructured input is often the biggest bottleneck in automation, and offloading that cognitive load to an LLM before the main business logic kicks in is absolutely the right pattern.

I’d love to add a few advanced tips to this architecture that have saved me a ton of headaches when running these pipelines in production:

  • Robust Error Routing: Even with strict prompts and low temperatures, LLMs can occasionally hallucinate keys or break JSON formatting. Using a Code node immediately after the HTTP request with a try/catch block to validate the JSON ensures that bad payloads don’t silently corrupt downstream data. I highly recommend configuring the node settings to use Continue On Fail and routing the error output to a dedicated Slack/Discord alert so a human can step in.

  • Modularize with Sub-Workflows: If you’re building a massive CRM enrichment or lead-scoring pipeline, it’s best to isolate this messy extraction step inside an Execute Workflow node. Pass the raw text into the sub-workflow, and configure it to strictly return the cleaned JSON to the parent workflow. This keeps the main canvas incredibly clean and lets you reuse the exact same extraction logic across different triggers (emails, webhooks, or direct messages).

  • Implement Fallback Logic: Since API endpoints can occasionally time out or face rate limits, you can connect the error output of your primary HTTP Request node to a secondary LLM endpoint. If the first extraction fails, it automatically retries with a fallback model, ensuring your data pipeline remains highly available.

Spot on regarding enforcing the exact JSON schema in the prompt rather than a vague instruction. Boring consistency really is the ultimate goal here! Thanks for sharing this detailed write-up.