The short version
- Build the agent as a hybrid flow: a custom Python component does the real auth → token → chained HTTP calls and
jsonschemavalidation; the Groq LLM (openai/gpt-oss-120b) is used only to parse cURLs and explain failures. The verifiable work is deterministic; the LLM never invents an HTTP response or a passing check. - The native API Request component can chain calls and inject a token, but its dynamic header/body injection is partially buggy and it has no cross-call state — so a single custom component using Python
requestsis the robust path for the token-reuse chain. - The flow JSON must use Langflow's exact serialization — the right top-level keys,
NodeName-UUIDids, and edge handles encoded with every"replaced by theœcharacter — or import fails with HTTP 422.
The architecture to build
The verified pattern from the first two agents (Input → Prompt → Groq → Structured Output → Output) is right for the LLM-facing parts. But a contract validator has a hard requirement: the HTTP calls and schema checks must be real. So the real work goes in code:
- Chat Input — user pastes the cURL commands.
- LLM parse stage (Prompt → Groq → Structured Output) — converts messy cURLs into a clean structured request plan with per-call contract expectations. A legitimate LLM job.
- Custom Python component — receives the plan, performs the real auth call, extracts the token, injects
Cookie: token=<token>on the protected calls, fires every request withrequests, validates each response withjsonschema, returns a structured verdict. - Second LLM stage — turns the raw pass/fail data into a readable explanation of why each call failed.
- Chat Output — the final verdict report.
Native API Request component
In Langflow 1.7.x the component class is APIRequestComponent (type id APIRequest), under lfx.components.data.api_request (the legacy langflow.components… path still resolves). Its template carries mode, urls, curl, method, query_params, body, headers, timeout, follow_redirects (now defaults to false for SSRF protection), and include_httpx_metadata.
Headers and body are nested-dict inputs that can accept an upstream Data object, but in practice only the URL is reliably dynamic — header/body injection from upstream has open bugs, and the component holds no state between calls. The method dropdown has historically omitted DELETE. For a token-reuse chain, prefer the custom component.
The custom component
There is no native component that carries an auth token across multiple API Request nodes. The clean approach is one custom Python component using requests (a core Langflow dependency — importable without being declared).
from lfx.custom.custom_component.component import Component
from lfx.io import DataInput, MultilineInput, MessageTextInput, SecretStrInput, IntInput, Output
from lfx.schema.data import Data
import requests, jsonschema
class APIContractValidator(Component):
display_name = "API Contract Validator"
description = "Runs a real auth-then-validate API chain and validates each response against a JSON Schema."
icon = "ShieldCheck"
name = "APIContractValidator"
inputs = [
DataInput(name="request_plan", display_name="Request Plan"),
MultilineInput(name="base_url", display_name="Base URL",
value="https://restful-booker.herokuapp.com"),
MessageTextInput(name="username", display_name="Username", value="admin"),
SecretStrInput(name="password", display_name="Password", value="password123"),
IntInput(name="timeout", display_name="Timeout", value=30),
]
outputs = [Output(name="report", display_name="Verdict Report", method="run_validation")]
def run_validation(self) -> Data:
session = requests.Session()
# 1) auth -> token ; 2) iterate plan, inject Cookie: token=<token> on writes
# 3) fire real requests ; 4) jsonschema.validate each response
# 5) return Data(data={"results": [...]})
...Inputs come from lfx.io (DataInput, MessageTextInput, MultilineInput, SecretStrInput, IntInput, …). Outputs are a class-level list, each mapping to a method returning Data or Message. Use a requests.Session() so cookies thread through automatically.
JSON Schema validation inside Langflow
There is no native JSON-Schema validation component. Structured Output coerces an LLM response into a schema; it does not validate an external API response against a strict schema. Use the jsonschema library inside the custom component:
v = jsonschema.Draft7Validator(schema)
errors = [f"{'/'.join(map(str, e.path))}: {e.message}"
for e in v.iter_errors(response_json)]
schema_ok = len(errors) == 0jsonschema is a transitive dependency in most Langflow installs and requests is core, so both import without extra setup in a standard install. On the standalone lfx package or Langflow Desktop, pin jsonschema in the instance requirements.txt to be safe. Note: format: "date" is only enforced with a format checker — otherwise add a regex pattern for YYYY-MM-DD.
The Restful Booker contract
Base URL https://restful-booker.herokuapp.com. The sandbox ships with 10 records and resets every 10 minutes.
| Endpoint | Auth | Status | Response shape |
|---|---|---|---|
POST /auth | — | 200 | {token} — but 200 even on bad creds, so check the body |
GET /booking | — | 200 | array of {bookingid} |
GET /booking/{id} | — | 200 | full booking object |
POST /booking | — | 200 | wrapped: {bookingid, booking} |
PUT /booking/{id} | Cookie: token= | 200 | updated booking object |
PATCH /booking/{id} | Cookie: token= | 200 | full booking object |
DELETE /booking/{id} | Cookie: token= | 201 | status only |
The booking object: firstname (string), lastname (string), totalprice (integer), depositpaid (boolean), bookingdates (object with checkin/checkout date strings), additionalneeds (string, optional). The create response wraps it in {bookingid, booking}; the auth response is {token}.
201 Created, not 200/204. And the live instance only reliably accepts the Cookie token form — the Basic-auth alternative in the docs often returns 403. Validate against what the API does, not just what it documents.Groq model + Structured Output wiring
The Groq component generates text via ChatGroq and offers a dual output: "Model Response" (Message) and "Language Model" (LanguageModel). Key fields: api_key, model_name (dropdown populated from Groq's /models), base_url (https://api.groq.com/openai/v1), temperature. It needs langchain-groq.
openai/gpt-oss-120b is a current Groq production model (MoE, 120B total / ~5.1B active, 128k context, Apache-2.0). To feed Structured Output, switch the Groq output to Language Model and connect that to Structured Output's Language Model input, alongside the Message you want to extract from.
Cookie-based auth
Canonical form: literal header Cookie: token=<token>, where the token is the value from POST /auth. A requests.Session() carries cookies for you. Authenticate once, attach the token to every mutating request, re-auth on 401/403.
Teaching analogy: the token is a hotel key card — collected once at check-in (auth), presented for every privileged action (PUT/PATCH/DELETE), and never assumed valid without a real round-trip.
Flow JSON serialization — avoiding 422
Top-level keys: data, description, endpoint_name, id, is_component, last_tested_version, name, tags. data holds nodes, edges, viewport. Nodes are genericNode with NodeName-UUID ids; node.data.type must match a registered component.
The critical part is edge encoding. Each edge's sourceHandle/targetHandle string is the decoded JSON re-serialized with every " replaced by œ (U+0153):
"sourceHandle": "{œdataTypeœ:œChatInputœ,œidœ:œChatInput-jFwUmœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}"The edge source must equal the sourceHandle's id; target must equal the targetHandle's id; both must be real node ids. Common 422 causes: corrupted or transliterated œ characters, missing nodes/edges, sending data as a string, or a data.type that doesn't match a registered component. Always read the detail array in the 422 body — it pinpoints the first failing field.
Build it in stages
- Stage 1 — the custom component in isolation. Run it with a hardcoded plan against Restful Booker; confirm real status codes and real
jsonschemaerrors. A deliberately wrong schema must produce a populated error. - Stage 2 — the parse stage. Chat Input → Prompt ("convert these cURLs to a JSON array") → Groq (output = Language Model) → Structured Output → into the component's request_plan.
- Stage 3 — the explanation stage + output. Feed the verdict into a second Prompt + Groq + Structured Output, then Chat Output.
- Stage 4 — export and validate. Export from the UI first (guarantees valid encoding), test import before any API call.
Caveats
- Restful Booker resets every 10 minutes and is a shared sandbox with intentional bugs — re-create a booking at the start of each run so you own a known id.
- Basic auth is unreliable on the live instance; use the Cookie token form.
- Native API Request header/body injection is partially buggy and its method dropdown may omit DELETE — another reason to use code.
- gpt-oss-120b + strict structured output has a known incompatibility in some LangChain versions; verify in your environment or use JSON mode.
- Version drift: exact import paths, the edge-id prefix, and whether
jsonschemaships by default vary by patch release. Confirm against your installed version's/docsand each component's in-UI Code view before finalizing. - The
œcharacter is U+0153; ensure your editor saves UTF-8 and does not auto-correct it back to".