Where this sits
The batch has n8n behind it and one Langflow session. Today is the second, and it opened on positioning rather than on the canvas.
The argument, in short. Langflow is open source, and it is already in production: two projects at the instructor's own company, one for RCA and one for flaky tests, running around the clock. It will arrive at your company too, and the person who brings it should be you. Not because the tool is hard, but because opportunity follows visibility. If your manager does not know you can build this, the problem statement goes to someone else. The phrasing from class: learn it, build it, then show the proof of concept.
One learner had already done exactly that, a defect insight agent with clustering and a chat interface that leadership is now using. That is the bar.
A promise was extracted from the room: three agents, today. An RCA bot, a bug triage agent and a flaky test finder. The instructor's count was that only nine people had posted on the last task, and he said plainly that this is the part that hurts. Meeti is sending a form to track which agents each person has actually built in n8n and Langflow.
The canvas, and what is on it
Langflow is a drag-and-drop builder over the same idea as n8n: trigger, action, result. The panel is on the left rather than the right, and pressing Escape gets you from the chat view back to the canvas.
What is in that panel, and worth knowing before you build:
- Inputs and triggers. Chat input, chat output, and a webhook, which starts the flow when something makes a request to its URL.
- Data. API request, SQL, URL fetch, file access, web search.
- Models. DeepSeek, Groq and the rest, each already a component. Nothing to code.
- Guardrails, which screen prompts for PII, jailbreaks and offensive content.
- Tool mode. Any component can be switched into it, which is how it becomes a tool an agent can pick up. That is the brain, memory, tools shape from the earlier class.
- Custom components, where you write Python if nothing on the shelf fits. Section 8 needed one.
Under discover more there are components published by other companies, Jira and Atlassian among them, free. The Jira one runs through Composio, which is a paid service with a free tier, so the class took the other route and called the REST API directly.
Version one: four components
The first build ignores Jira completely and proves the idea:
Chat input -> DeepSeek -> Chat output
^
system prompt: you are a QA bug triage assistant
Paste a real bug report into the playground and it comes back with a classification, severity, priority, QA comments and a triage suggestion. Working, and useless, because a human still has to fetch the ticket and paste it.
The prompt template is worth adding even here. The system message can live on the model component, but pulling it into its own prompt template component makes the flow modular: when the prompt changes you edit one box instead of opening the model's settings. That template becomes load-bearing two sections from now.
Fetching the ticket, and the auth trap
Replace the chat input with an API request component pointed at Jira, and the first attempt returns an HTML page rather than JSON. So does the second. This took a real chunk of the session, and the answer is worth more than the flow:
Jira's REST API does not want your API token in the header. It wants HTTP basic auth, which means the header value is the word Basic followed by the base64 encoding of email:token, not the token on its own. Asked in class whether the token is the Jira API token: no. It is the combination, your account email and the token, joined by a colon and encoded.
# the value the Authorization header actually wants
printf 'you@example.com:YOUR_API_TOKEN' | base64
GET https://<your-site>.atlassian.net/rest/api/3/issue/VWO-49
Authorization: Basic <that base64 string>
Accept: application/json
Two practical notes from the debugging. Check it in Postman first: importing the curl into Postman is how the class established that the credentials were fine and the problem was elsewhere. And Postman strips the auth header on import, so re-add it before concluding anything.
Why the parser is not optional
The API response is JSON. The model input is text. Langflow will not let you wire one into the other, and that is a type rule rather than a bug. The parser sits between them and turns the response into something a prompt can carry.
Once parsed, the ticket goes into a prompt template as a variable:
You are a senior QA analysing a bug report.
Give severity, priority and QA comments.
Bug report:
{result}
Run it and the prompt that reaches DeepSeek contains the whole Jira JSON, the model reads it, and the triage comes back for that specific ticket.
Making the key dynamic
The flow still had the ticket ID hardcoded in the URL, which the room spotted immediately. The fix is small and it is the whole trick:
https://<your-site>.atlassian.net/rest/api/3/issue/{issue_key}
{issue_key} in single quotes, not double, makes it a variable on the prompt template, and the chat input feeds it. Send VWO-50, the URL becomes the VWO-50 issue, and the triage comes back for VWO-50. Send VWO-51 and it does it again.
The repository keeps both versions on purpose, which makes the difference easy to see:
| Flow | Where the ticket comes from | Usable from a UI? |
|---|---|---|
01_LangFlow_Simple_HelloWorld |
nothing, chat input straight to the model | the hello world |
03_AI4X_003_..._Agentic |
hardcoded VWO-49 in the URL |
no, the input is ignored |
004_AI4X_004_..._Via_UI |
chat input feeds {issue_key} |
yes |
Flow 03 has no chat input at all, so anything you send lands nowhere and every call returns the same VWO-49 triage. That is the bug the dynamic version fixes.
The flow is already an API
This is the part that changes what a flow is for. Every Langflow flow is served at POST /api/v1/run/<flow-id>. Open API access on the flow, create a Langflow API key, and you have a curl.
The call, as the repository's own front end makes it:
const res = await fetch('/api/run', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
output_type: 'chat',
input_type: 'chat', // must be "chat"
input_value: 'VWO-51', // the ticket key; there is no "issue_key" field here
session_id: 'ui-1a2b3c',
}),
});
const data = await res.json();
const text = data.outputs[0].outputs[0].results.message.data.text;
input_value carries the ticket key, not a field named issue_key. The {issue_key} variable exists inside the prompt template; from outside, the flow has one chat input and the value goes in as input_value. Getting this wrong is the difference between a working call and the same VWO-49 answer every time.
A page in front of it
Pasting the curl into a coding agent and asking for "a lightweight React UI with a run button" produced a working front end in a couple of minutes: type a ticket key, press Run, read the triage. That is now in the repository at chapter_09_LangFlow/ui_bugtriage/.
The part worth reading before you deploy your own, straight from the comments in that code:
A Vercel function cannot see your laptop. The front end calls its own /api/run, which forwards to Langflow with the API key kept server-side. If LANGFLOW_URL is set to http://localhost:7860 on a deployed site, localhost there means the function's own container, not your machine. Expose Langflow with a tunnel (cloudflared tunnel --url http://localhost:7860) or host it, and use that address. The repo's proxy checks for exactly this and returns a readable error instead of a timeout.
The custom component, and the limit that forced it
The last build tried to add Claude Code as the brain via an API request component, and it would not connect. The reason is the same type rule as the parser, in the other direction:
Asked live, and answered by the model the class was debugging with: the body port on an API request is a data port, while a prompt template's output is a message. Langflow only lets you snap a message wire into a message port, so that connection is not possible at all. The way through is a custom component, a small piece of Python that takes the message, builds the JSON body itself, and makes the call.
DeepSeek generated that component, it was pasted into a new custom component on the canvas, and it worked on the first run. The code is in the repository as CustomCommandCode.json.
Two things were still broken when the session ended, and they are reported here as they were: the curl-based version of that same call kept returning empty content, and the equivalent flow through the plain URL form worked instead. The instructor said he would fix it and share it rather than leave it hanging.
The debugging was left in on purpose. In the instructor's words, this is how you actually do it: you ask, you check in Postman, you read the error, you try the other route. The session was deliberately not edited into a clean demo.
Tasks and announcements
Today
- Build three agents. An RCA bot, a bug triage agent, and a flaky test finder. Minimum three, posted to the SDET club thread.
- Post them on LinkedIn. Your n8n flows and your Langflow flows.
The next build, with a hint
- A flaky test case finder. The hint given in class: use the read file component to ingest a JSON test result file, then hand it to the model the same way the ticket was handed over here. Two files, compared, is the shape.
Announcements
- A mandatory live test covering everything in n8n and Langflow so far.
- A separate video on installing Langflow with Docker is being recorded rather than done live. If you are on Docker already, use a volume so your flows survive a restart.
- The exported flows and the custom component are being shared with the batch.
- On spend: five dollars on DeepSeek lasts months at this usage. Groq is free, and Google's lighter Gemini models have a free tier. The brain does not matter much here; the flow does.
Repository: AITesterBlueprin4x received chapter_09_LangFlow/ with the four exported flows (hello world, the agentic hardcoded version, the UI version and the custom Claude Code component) and the ui_bugtriage React front end with its Vercel proxy.