Webhooks and returning formatted text?

Purpose of the post

Looking for suggested fixes or better solutions to handle the workflows output whilst maintaining the formatting.

Short context of my skill set …

I do not know javascript but have a solid grounding in programming and have built a number of non-web focused python projects. I am very familiar with backend systems, automation systems and containers (docker etc).

Goal

To build a web based system that allows the user to choose options from a dropdown list or type free hand text into a webpage, have the workflow process the choice and return a formatted response that can be easily cut and pasted in to a word document. Running a copy of the workflow via a chat trigger gives exactly what I want in the chat response window (text and format) so I can just copy paste to a Word doc.

Progress

  • Basic web frontend: Working.
  • n8n workflow: Working.
  • Receive result in Word friendly formatting: WIP

Issue

Formatting of the response so it can be easily transferred to a word document, retaining its formatting.

The output is from an ai agent (glm-4.7-flash:29.9B - local Ollama container) and is HTML formatted (regardless of what I ask for). THe agent puts the final result in a JSON kv object by my request in order to easily and consistently separate the result from all the rambling prior to producing what I require.

I am then using a second ai agent to strip the resulting JSON object from all the pages of ‘thinking‘ the agent likes to provide (regardless of my request for it not to). Doing this stage with regex was possible but very troublesome. Using a second agent worked out much easier in the end.

I am then using a code node that tries to make the string JSON compatible for the respond webhook to use it in its JSON return structure below.

{
  "jobId": "{{ $('Set SessionId').item.json.sessionId }}",
  "status": "Done",
  "data": "{{ $json.finalresultStr }}"
}

Unfortunately I am getting JSON format errors every time and it is driving me mad.

The ai result text is around 4700 characters and contains dates (dd\mm\yy), newlines (\n) and some other HTML formatting elements ( *, #, - etc).

Unoptimised js code to handle string to JSON string via regex

const text = $input.first().json.output; // Ensure 'output' is the correct field name

// 1. Remove Markdown code blocks
const cleanText = text.replace(/```json\n?/g, '').replace(/```\n?/g, '');

// 2. Normalize line endings
const normalizedText = cleanText.replace(/\r\n/g, '\n').replace(/\r/g, '\n');

//return {normalizedText};

// 3. Apply your regex
const regex = /(\{\n.{2}.Result.:..([\s\S]*?)\"\n\})(?:[\s\S]*?)/s;
const match = normalizedText.match(regex);

//return{match}

// 4. Parse and Clean the captured JSON
try {
    let resultStr = match[2];

    // return{match}
    //return{resultStr}
  
    const resultStr1 = resultStr.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
    //return{resultStr1}
  
    const resultStr2 = resultStr1.replace(/\//g, '\\/');
    //return{resultStr2}
  
    // const resultStr3 = resultStr2.replace(/\n/g, '\\\n');
    const finalresultStr = resultStr2.replace(/\\n/g, '\\\n');

    return {finalresultStr}
    //return { json: JSON.parse(finalresultStr) };
} catch (e) {
    return { json: { error: "JSON Parse Error: " + e.message } };
}

I have noticed that on loading the ‘finalresultStr‘ to the JSON object, by the respond webhook node, it has a habit of stripping the \n codes and then complains that the multiline string is not JSON compatible.

Using stringify does not help…

Thoughts to move forwards (feedback / suggestions welcome).

  1. Stick with this and find a solution for consistent formatted text via respond webhook.
  2. Save results to a web page and return a link (web server side setup etc ?).
  3. Save results as a file locally (host server) and user has to sftp it over (save as what format to keep formatting ?) .

I am working on supplying a copy of the ai output but it contains sensitive info I would rather not publish here. I have tried masking with regex (change all a-z & A-Z to a) but this generated invalid control codes. If some has a decent masking process (regex or other) then I can try and put it up here.

Thanks for taking the time to look and feel free to ask any on topic questions.

The JSON formatting issue is almost certainly caused by unescaped characters in the HTML string breaking the JSON structure. When you embed raw HTML directly into a JSON template like "data": "{{ $json.finalresultStr }}", n8n tries to slot the raw string in – and HTML is full of characters that blow up JSON (quotes, newlines, backslashes).

The fix: use a Code node to properly serialize the whole response object, instead of building the JSON manually in the Respond to Webhook node.

Replace your current Code node output with something like this:

const rawResult = $input.first().json.finalresultStr || '';
return [{
  json: {
    jobId: $('Set SessionId').first().json.sessionId,
    status: 'Done',
    data: rawResult
  }
}];

Then in your Respond to Webhook node, just return the entire item as JSON – don’t build a manual template. Set Response Body to Return All Fields. This way n8n handles the serialization and nothing gets mangled.

On the HTML vs Word formatting problem: the cleanest approach is to tell your AI agent to output markdown instead of HTML. Markdown survives the JSON pipeline much better, and tools like Pandoc can convert it to .docx server-side if you need actual Word files. If you’re staying browser-side, a markdown-to-clipboard trick works well too.

If the model keeps ignoring your formatting instructions, add this explicitly to the system prompt:

“Your final answer MUST be plain text with markdown formatting only. Never use HTML tags.”

Models respond better to “never” than to “don’t”.

The second AI agent you’re using to strip thinking chains is a solid pattern by the way – regex for that is a nightmare. Good call.

1 Like

@OMGItsDerek: Thanks for the very detailed response. Have been quite busy over the past few days but will take a look and give it the time it deserves tomorrow. Hope to give feedback on how successful (or not) I have been :grinning_face_with_smiling_eyes: .

1 Like

This is a classic webhook + JSON formatting headache and you’re actually really close to the solution.

The core problem is that when you embed a multiline HTML/markdown string inside a JSON value, any unescaped newlines break the JSON structure completely. stringify is the right instinct but you have to use it properly.

Instead of building your respond webhook body as a raw JSON template with {{ }} expressions, try letting n8n do the heavy lifting. In your Code node, return the data as a proper JS object and let n8n serialize it:

js

const text = $input.first().json.output;
return {
  json: {
    jobId: "your-session-id-here",
    status: "Done",
    data: text  // let JS handle the escaping
  }
}

Then in my Respond to Webhook node, just reference {{ $json.data }} — don’t hand-build the JSON string. n8n will serialize the whole object cleanly and the newlines won’t break anything.

Also for the date issue with dd/mm/yy — forward slashes don’t need escaping in JSON values, only in some regex contexts, so you can drop that replace step.

If you want the output to paste cleanly into Word, honestly the cleanest path is returning the data as HTML from your webhook and rendering it in your frontend then the user can copy from a styled div. Word handles pasted HTML formatting really well.

Happy to help you map this out properly if you want to hop on a quick call Calendly