<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Srijith Kartha</title>
    <description>The latest articles on DEV Community by Srijith Kartha (@srijith).</description>
    <link>https://dev.to/srijith</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F817166%2F1ad80698-5646-42e0-925f-0310181768ff.png</url>
      <title>DEV Community: Srijith Kartha</title>
      <link>https://dev.to/srijith</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/srijith"/>
    <language>en</language>
    <item>
      <title>Microsoft's Agent Governance Toolkit and Where Rynko Flow Fits In</title>
      <dc:creator>Srijith Kartha</dc:creator>
      <pubDate>Sun, 22 Mar 2026 02:29:54 +0000</pubDate>
      <link>https://dev.to/srijith/microsofts-agent-governance-toolkit-and-where-rynko-flow-fits-in-1ifd</link>
      <guid>https://dev.to/srijith/microsofts-agent-governance-toolkit-and-where-rynko-flow-fits-in-1ifd</guid>
      <description>&lt;p&gt;Microsoft just open-sourced the &lt;a href="https://github.com/microsoft/agent-governance-toolkit" rel="noopener noreferrer"&gt;Agent Governance Toolkit&lt;/a&gt;, a runtime governance platform that covers all 10 risks in the OWASP Agentic Top 10. I've spent the morning reading through the architecture, benchmarks, and OWASP compliance docs, and it's one of the most thorough agent governance framework I've seen from any company, open-source or otherwise.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Policy evaluation at 0.012ms latency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ed25519 cryptographic agent identity with trust scoring.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Four-tier execution rings with kill switches.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Circuit breakers and chaos engineering for reliability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Adapters for 12+ frameworks including LangChain, AutoGen, CrewAI, and Google ADK.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;6,100+ tests. MIT licensed.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the kind of infrastructure that the agentic ecosystem desperately needs, and Microsoft giving it away for free accelerates the entire space.&lt;/p&gt;

&lt;p&gt;It also makes me more confident about the bet we've been making at Rynko, because the toolkit solves a genuinely hard set of problems that we don't solve — and it leaves room for the specific problem that we do.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Toolkit Does Well
&lt;/h2&gt;

&lt;p&gt;The toolkit has four components, and each one addresses a real production concern that teams building agentic systems struggle with.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agent OS&lt;/strong&gt; is the policy engine. Every agent action passes through it before an execution. You define capabilities like which tools the agent can call, resource limits like token budgets, API call caps and content policies. It evaluates these at sub-millisecond latency — 72,000 policy evaluations per second for single rules, 31,000 for 100-rule policies. Custom policies can be written in OPA/Rego or Cedar, which means teams can reuse their existing policy infrastructure rather than learning a new DSL, a thoughtful design choice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AgentMesh&lt;/strong&gt; handles identity and inter-agent trust. Every agent gets an Ed25519 cryptographic credentials. Trust scores on a 0–1000 scale determine what an agent can do eg. a score of 900+ gets verified partner access, below 300 gets read-only. The communication between agents is encrypted through trust gates, and it bridges A2A, MCP, and IATP protocols. The trust scoring model is particularly well thought out, eg. new agents default to 500 and progress based on compliance history, which mirrors how you'd onboard a new team member with gradually expanding permissions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agent Runtime&lt;/strong&gt; is the execution supervisor. It uses four privilege rings to isolate what agents can touch. Saga orchestration is used to coordinate multi-step operations. Kill switches terminate non-compliant agents and Append-only audit logs record everything for forensic replay.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agent SRE&lt;/strong&gt; provides reliability engineering. SLO enforcement, error budgets, circuit breakers are available to prevent cascading failures, replay debugging and chaos engineering. The production observability patterns you'd expect from a team that runs Azure at scale.&lt;/p&gt;

&lt;p&gt;All four components work together to answer a fundamental question: &lt;strong&gt;is this agent allowed to do what it's trying to do, and is it doing it safely?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is genuinely hard infrastructure to build correctly. Identity, policy enforcement, execution isolation, and reliability engineering each have deep rabbit holes, and Microsoft has the engineering depth to go down all of them properly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Flow Adds a Complementary Layer
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk9vzhx3dq6llxuyz6mip.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk9vzhx3dq6llxuyz6mip.png" alt="Where flow fits in" width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The toolkit governs agent &lt;em&gt;behavior&lt;/em&gt; — permissions, identity, execution boundaries, reliability. Flow governs agent &lt;em&gt;output&lt;/em&gt; i.e. the actual data the agent produces when it completes an action.&lt;/p&gt;

&lt;p&gt;These are different concerns. The toolkit ensures the agent is authorized and operating safely. Flow ensures the data the agent produces is correct and hasn't been tampered with before reaching the downstream system.&lt;/p&gt;

&lt;p&gt;One reasonable question to ask would be: couldn't AgentMesh's trust gates or the Agent OS policy engine handle data validation too? Technically, you could write OPA/Rego policies that inspect payload fields — Rego is expressive enough to check &lt;code&gt;input.payload.amount &amp;gt; 0&lt;/code&gt;. But policy engines are designed to return allow/deny decisions, not structured validation errors with field-level messages that an agent can use to self-correct and resubmit. You'd also be mixing authorization concerns with domain-specific business logic in the same policy files. Also, you wouldn't get HMAC-based payload verification or human approval routing. It's a bit like using a firewall for input validation — it can inspect packet contents, but that doesn't make it the right layer for checking whether an invoice total matches its line items.&lt;/p&gt;

&lt;p&gt;Think about the OWASP compliance mapping in the toolkit. ASI-05 addresses unexpected code execution through privilege rings and sandboxing. This makes sure that the agent can't run arbitrary code. That's the right control for that risk. But once the agent produces a result through an approved tool call - an invoice, a purchase order, a compliance report - there's a different question to answer: is the data in that result actually correct?&lt;/p&gt;

&lt;p&gt;An agent can be fully authorized, properly authenticated, running within its privilege ring, with no circuit breaker tripped. The policy engine approved the action. And the agent still submits &lt;code&gt;"currency": "usd"&lt;/code&gt; instead of &lt;code&gt;"USD"&lt;/code&gt;, calculates a total that's off by a rounding error, or drops a required field. These are domain-specific data quality issues that a behavioral governance layer isn't designed to catch, and honestly shouldn't try to, that would mix concerns and bloat the policy engine with domain logic.&lt;/p&gt;

&lt;p&gt;This is what Flow was built for. You define a gate with a schema and business rules specific to your domain, and the agent's output gets validated before it reaches the downstream system. Validation Failures return structured errors which the agent can use to self-correct. Passed validations return a &lt;code&gt;validation_id&lt;/code&gt; - an HMAC-SHA256 hash of the validated payload which the downstream system can independently verify.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the Two Layers Work Together
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Furbx7upvbg4pedeb71py.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Furbx7upvbg4pedeb71py.png" alt="Governance Layers" width="800" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The distinction maps to how we think about security in traditional systems. Authentication and authorization tell you who's making a request and whether they're allowed to. Input validation tells you whether the data they're sending is well-formed and correct. You've always needed both. The agentic world isn't different.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;Microsoft Toolkit&lt;/th&gt;
&lt;th&gt;Rynko Flow&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Identity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Who is this agent?&lt;/td&gt;
&lt;td&gt;Ed25519 credentials, trust scores&lt;/td&gt;
&lt;td&gt;API key auth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Authorization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Can it call this tool?&lt;/td&gt;
&lt;td&gt;Policy engine, capability model&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Execution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Is it running safely?&lt;/td&gt;
&lt;td&gt;Privilege rings, sandboxing&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reliability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Will failures cascade?&lt;/td&gt;
&lt;td&gt;Circuit breakers, SLOs&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Output correctness&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Is the data valid?&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Schema + business rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Output integrity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Was the data tampered?&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;HMAC verification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Human oversight&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Should a person review?&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Approval routing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The toolkit handles the rows above the line. Flow handles the rows below it. Together, they cover the pipeline end to end.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Practical Example
&lt;/h2&gt;

&lt;p&gt;Say you have an order processing agent running in an environment with the toolkit deployed. The policy engine confirms the agent has permission to submit orders. AgentMesh verified its identity. The runtime supervisor confirmed it's operating within its privilege ring.&lt;/p&gt;

&lt;p&gt;The agent submits this order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"order_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORD-2847"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"vendor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Acme Corp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"usd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"line_items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the toolkit's perspective, everything checks out. The agent was authorized, authenticated, and operating within bounds. The policy engine approved the action. And it should approve it — the toolkit's job is to enforce behavioral governance, not validate business data.&lt;/p&gt;

&lt;p&gt;Flow picks up where the toolkit leaves off. A gate with the appropriate schema and rules catches three issues:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"errors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Must be &amp;gt;= 0"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Must be one of: USD, EUR, GBP"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"rule"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"line_items.length &amp;gt; 0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Must have at least one line item"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent self-corrects using the structured feedback, resubmits, and gets a &lt;code&gt;validation_id&lt;/code&gt; on success. The downstream system verifies the ID before accepting the data. The toolkit made sure the right agent submitted the order safely. Flow made sure the order itself was correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance — Both Layers Are Essentially Free
&lt;/h2&gt;

&lt;p&gt;One thing the toolkit's benchmarks highlight is that governance overhead should be invisible relative to LLM latency. Their policy evaluation adds 0.01–0.1ms. An LLM API call takes 200–3,000ms. I think they're exactly right about this — governance shouldn't be the bottleneck, and at those numbers it never will be.&lt;/p&gt;

&lt;p&gt;Flow operates at a different timescale because it's doing more work per evaluation — parsing payloads, validating schemas against variable arrays, running expression-based business rules through a recursive descent parser. Our benchmarks show ~50ms server-side validation for enterprise-scale payloads (21 schema variables, 10 business rules, 900 line items in a single payload). For typical payloads (a few KB), it's single-digit milliseconds.&lt;/p&gt;

&lt;p&gt;Combined, both layers add maybe 50–60ms to a pipeline where the LLM inference took 500–3,000ms. You're paying a negligible cost for behavioral governance and output validation together.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bigger Picture
&lt;/h2&gt;

&lt;p&gt;Between the OWASP Agentic Top 10, the &lt;a href="https://blog.rynko.dev/how-rynko-flow-maps-to-the-aws-agentic-ai-security-scoping-matrix" rel="noopener noreferrer"&gt;AWS Agentic AI Security Scoping Matrix&lt;/a&gt;, Snapchat's &lt;a href="https://blog.rynko.dev/what-snapchat-auton-framework-means-for-ai-agent-validation" rel="noopener noreferrer"&gt;Auton framework&lt;/a&gt;, and now Microsoft's toolkit, the industry is converging on something I think is important: agent governance is not a single problem with a single solution. It's a stack of specialized layers, each addressing different risks at different points in the pipeline.&lt;/p&gt;

&lt;p&gt;Microsoft releasing this toolkit validates the category in a way that benefits everyone building in the space. When the company that runs Azure tells the world "agent governance is infrastructure, here's our reference implementation for free," it moves the conversation from "do we need agent governance?" to "which layers do we still need to add?"&lt;/p&gt;

&lt;p&gt;We think output validation is one of those layers. Not because the toolkit missed something, but because domain-specific data correctness is a separate concern that deserves its own specialized tooling. Checking whether an invoice has the right currency code, whether an order total matches its line items, or whether a compliance report includes all required fields isn't a policy evaluation problem. It's a schema and business rule problem with optional human review in the loop.&lt;/p&gt;

&lt;p&gt;That's what we built Flow to handle. If you're deploying the Agent Governance Toolkit and want to add output validation to the pipeline, try dropping a &lt;a href="https://app.rynko.dev/flow/gates" rel="noopener noreferrer"&gt;Flow gate&lt;/a&gt; between the governed agent and your downstream system. The free tier gives you 500 validation runs per month and three gates — enough to see how the two layers work together in practice.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Rynko Flow is a validation gateway for AI agent outputs.&lt;/em&gt; &lt;a href="https://app.rynko.dev/signup" rel="noopener noreferrer"&gt;&lt;em&gt;Try it free&lt;/em&gt;&lt;/a&gt; &lt;em&gt;or&lt;/em&gt; &lt;a href="https://docs.rynko.dev/flow/getting-started" rel="noopener noreferrer"&gt;&lt;em&gt;read the docs&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>microsoft</category>
      <category>agents</category>
      <category>rynko</category>
    </item>
    <item>
      <title>IBM's $11 Billion Confluent Acquisition, AWS + Cerebras, and Where Output Validation Fits In</title>
      <dc:creator>Srijith Kartha</dc:creator>
      <pubDate>Wed, 18 Mar 2026 08:08:21 +0000</pubDate>
      <link>https://dev.to/srijith/ibms-11-billion-confluent-acquisition-aws-cerebras-and-where-output-validation-fits-in-2jj6</link>
      <guid>https://dev.to/srijith/ibms-11-billion-confluent-acquisition-aws-cerebras-and-where-output-validation-fits-in-2jj6</guid>
      <description>&lt;h1&gt;
  
  
  IBM is solving real-time data for agents. AWS is solving inference speed. Both are foundational. Here's how Rynko Flow adds an output validation layer to complement what they're building.
&lt;/h1&gt;

&lt;p&gt;Two announcements in the same week paint a clear picture of where enterprise AI infrastructure is headed, and both of them are exciting.&lt;/p&gt;

&lt;p&gt;IBM closed its &lt;a href="https://www.linkedin.com/pulse/welcome-confluent-ibm-xtjqe" rel="noopener noreferrer"&gt;$11 billion acquisition of Confluent&lt;/a&gt;, the Kafka-based streaming platform used by 40% of Fortune 500 companies. The thesis is sound: enterprises moving from AI experimentation to production need live, continuously flowing data — not batch exports that arrive hours late. As Rob Thomas (IBM SVP) put it, "AI decisions need to happen just as fast" as the transactions generating the data. That's exactly right, and Confluent is the best platform in the world for making it happen.&lt;/p&gt;

&lt;p&gt;Meanwhile, &lt;a href="https://www.cerebras.ai/press-release/awscollaboration" rel="noopener noreferrer"&gt;AWS announced a collaboration with Cerebras&lt;/a&gt; to bring wafer-scale inference to Amazon Bedrock. The CS-3 delivers thousands of times more memory bandwidth than the fastest GPU, targeting the decode bottleneck that slows agentic workloads. Andrew Feldman (Cerebras CEO) called it "blisteringly fast inference." Their disaggregated architecture pairs Trainium for compute-heavy prefill with Cerebras WSE for bandwidth-heavy token generation — an order of magnitude faster inference than what's available today. For anyone building real-time agentic workflows, this is a big deal.&lt;/p&gt;

&lt;p&gt;These are the kind of infrastructure investments that make agentic systems practical at enterprise scale. They also got me thinking about where Rynko Flow fits into this picture.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pipeline and Where Each Layer Contributes
&lt;/h2&gt;

&lt;p&gt;The enterprise AI pipeline looks roughly like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbfwkv2tkx0zqnhxvu4m0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbfwkv2tkx0zqnhxvu4m0.png" alt="Simple Pipeline" width="800" height="88"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;IBM + Confluent handle the input: getting live, governed, trustworthy data to the agent. AWS + Cerebras handle the processing: making the agent produce output fast enough for real-time operations. Both are necessary — an agent making decisions on stale data is worse than no agent at all, and an agent that takes 30 seconds to respond isn't useful for time-sensitive workflows.&lt;/p&gt;

&lt;p&gt;What we've been focused on at Rynko is the next step in that pipeline: once the agent processes that real-time data at speed and produces a result — an invoice, a purchase order, a compliance report — how do you validate that the result is correct before it reaches the downstream system?&lt;/p&gt;

&lt;p&gt;This is a genuinely different problem from data freshness or inference speed, and it's the problem we built Flow to solve. Even with perfect input data, agents can submit &lt;code&gt;"usd"&lt;/code&gt; instead of &lt;code&gt;"USD"&lt;/code&gt;, produce a total that's off by a rounding error, or silently drop a required field. The data flowing in was pristine. The processing was fast. The output still needs a checkpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Flow Adds to the Pipeline
&lt;/h2&gt;

&lt;p&gt;Flow is a validation gateway that sits between the agent's output and your downstream systems. You define a gate with a schema and business rules, the agent submits its output, and Flow validates it before the data moves forward. Failed submissions return structured errors the agent can use to self-correct. Passed submissions return a tamper-proof &lt;code&gt;validation_id&lt;/code&gt; that the downstream system can verify to confirm nothing was modified in transit.&lt;/p&gt;

&lt;p&gt;Say you have an order processing agent. Confluent is streaming real-time order events from your POS systems, inventory databases, and payment providers. The agent processes these events and produces a purchase order to send downstream. Here's the Flow gate that checks the agent's output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Schema:
  - order_id: string, required
  - vendor: string, required
  - amount: number, required, min 10
  - currency: string, required, enum [USD, EUR, GBP]
  - line_items: array of objects, required

Business Rules:
  - amount &amp;gt; 0 ("Order amount must be positive")
  - amount &amp;lt;= 100000 ("Single order cannot exceed $100,000")
  - line_items.length &amp;gt; 0 ("Must have at least one line item")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent submits its payload. Flow validates it against the schema and evaluates every business rule. If the agent submitted &lt;code&gt;amount: -500&lt;/code&gt;, it gets back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"validation_failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"errors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"rule"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"amount &amp;gt; 0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Order amount must be positive"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent self-corrects and resubmits. When validation passes, the response includes a &lt;code&gt;validation_id&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"validated"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"validation_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"val_4f546e9bcb76f120c4984d72"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;validation_id&lt;/code&gt; is an HMAC-SHA256 hash of the validated payload, computed using canonical JSON serialization with recursively sorted keys. This means even if the payload passes through multiple systems that reorder the JSON keys or reformat the whitespace, the verification still works. The downstream system receives the payload and the &lt;code&gt;validation_id&lt;/code&gt; from the agent, then calls Flow to verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.rynko.dev/api/flow/verify &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "validation_id": "val_4f546e9bcb76f120c4984d72",
    "payload": { "order_id": "ORD-001", "vendor": "Acme", "amount": 500, ... }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"verified"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"runId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"550e8400-e29b-41d4-a716-446655440000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"gateName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Order Validation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"gateSlug"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"order-validation"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the agent tampered with the payload after validation — changed the amount, added a field, removed a required value — verification returns &lt;code&gt;verified: false&lt;/code&gt;. The downstream system knows not to trust the data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validation Doesn't Have to Be a Bottleneck
&lt;/h2&gt;

&lt;p&gt;One concern I hear is whether validation adds meaningful latency to a real-time pipeline. We benchmarked Flow against enterprise-scale payloads — the kind of data you'd see flowing through Kafka in a large manufacturing or logistics operation.&lt;/p&gt;

&lt;p&gt;We tested with a Sterling Commerce OMS-style order payload: 21 schema variables, 10 business rules, 900 order line items. The payloads were around 9MB for XML and 7.3MB for JSON.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;XML (9.1 MB)&lt;/th&gt;
&lt;th&gt;JSON (7.3 MB)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total round-trip&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4,989 ms&lt;/td&gt;
&lt;td&gt;4,401 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Server-side validation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~50 ms&lt;/td&gt;
&lt;td&gt;~50 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Network upload (at ~800 KB/s)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~3,800 ms&lt;/td&gt;
&lt;td&gt;~3,100 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The validation itself — schema checks plus 10 business rule evaluations — takes about 50 milliseconds. The rest is network transfer. At typical payload sizes (a few KB for a single order or invoice), the validation adds single-digit milliseconds. For a 30-line order at 0.3MB, the total round-trip was 1,960ms with most of that being upload time over a standard connection.&lt;/p&gt;

&lt;p&gt;Server-side processing is fast because Flow runs validation in-memory: schema validation against a pre-compiled variable array, then expression evaluation through a recursive descent parser for each business rule. No database queries during validation. Persistence runs asynchronously after the response is sent — payloads go to S3, run metadata goes to Postgres, both fire-and-forget so the agent gets its response immediately.&lt;/p&gt;

&lt;p&gt;For Kafka-speed pipelines where even 50ms matters, Flow also supports webhook delivery — validation happens, and the validated payload is pushed directly to your endpoint without the agent needing to relay it. That eliminates the agent-as-middleman entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  All Three Layers Together
&lt;/h2&gt;

&lt;p&gt;Here's how I'd architect an agentic pipeline with all three layers working together:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ax2cqwsnwjivvq0gsxq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ax2cqwsnwjivvq0gsxq.png" alt="Enterprise Pipeline" width="800" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Confluent handles data-in-motion: live events, governed, streaming at scale. Cerebras on Bedrock processes those events fast — the disaggregated Trainium+WSE architecture means agents produce structured output at speeds that make real-time workflows practical. Flow validates that output against your schema and business rules, returns errors for self-correction or a tamper-proof &lt;code&gt;validation_id&lt;/code&gt; on success. The downstream system verifies the &lt;code&gt;validation_id&lt;/code&gt; before accepting the data.&lt;/p&gt;

&lt;p&gt;Three separate problems, three separate layers. Each one does its part well. Confluent ensures the agent gets good data. Cerebras ensures the agent processes it fast. Flow ensures the output is correct before it reaches production systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters Now
&lt;/h2&gt;

&lt;p&gt;I want to be clear: Flow doesn't replace anything IBM, Confluent, AWS, or Cerebras are building. They're solving data infrastructure and inference speed — foundational problems that every enterprise needs addressed. These are massive, hard engineering challenges, and both acquisitions and partnerships reflect the kind of investment this space deserves.&lt;/p&gt;

&lt;p&gt;What Flow adds is a complementary output validation layer. As agents move from experimental to production, and as the data flowing through them gets faster and the inference gets cheaper, the volume of agent-generated outputs hitting downstream systems is going to increase significantly. Having a validation checkpoint in that pipeline — one that catches domain-specific errors, enforces business rules, and provides tamper-proof verification — becomes more valuable as the rest of the stack gets faster.&lt;/p&gt;

&lt;p&gt;AWS's Agentic AI Security Scoping Matrix (published November 2025) calls out many of the capabilities Flow provides: approval gateway enforcement, agent controls, audit trails, agency perimeters. We've &lt;a href="https://blog.rynko.dev/how-rynko-flow-maps-to-the-aws-agentic-ai-security-scoping-matrix" rel="noopener noreferrer"&gt;mapped Flow against every scope in that framework&lt;/a&gt; — it covers Scopes 2 and 3 well, with partial coverage at Scope 4 where fully autonomous agents need capabilities beyond what a validation gateway provides alone.&lt;/p&gt;

&lt;p&gt;If you're building agentic workflows on Kafka, Bedrock, or both, try dropping a &lt;a href="https://app.rynko.dev/flow/gates" rel="noopener noreferrer"&gt;Flow gate&lt;/a&gt; between your agent and your downstream system. The free tier gives you 500 validation runs per month and three gates — enough to see how output validation fits into your pipeline.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Rynko Flow is a validation gateway for AI agent outputs.&lt;/em&gt; &lt;a href="https://app.rynko.dev/signup" rel="noopener noreferrer"&gt;&lt;em&gt;Try it free&lt;/em&gt;&lt;/a&gt; &lt;em&gt;or&lt;/em&gt; &lt;a href="https://docs.rynko.dev/flow/getting-started" rel="noopener noreferrer"&gt;&lt;em&gt;read the docs&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ibm</category>
      <category>confluent</category>
      <category>aws</category>
      <category>rynko</category>
    </item>
    <item>
      <title>Teaching Gates to Learn: How We Built Intelligence Into Rynko Flow</title>
      <dc:creator>Srijith Kartha</dc:creator>
      <pubDate>Mon, 16 Mar 2026 07:14:12 +0000</pubDate>
      <link>https://dev.to/srijith/teaching-gates-to-learn-how-we-built-intelligence-into-rynko-flow-3ik8</link>
      <guid>https://dev.to/srijith/teaching-gates-to-learn-how-we-built-intelligence-into-rynko-flow-3ik8</guid>
      <description>&lt;h2&gt;
  
  
  Flow validates agent outputs against schemas and business rules. But when 60% of agents fail the same rule on first attempt, the gate should be telling you why — and helping you fix it.
&lt;/h2&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The key insight:&lt;/strong&gt; When agents fail and retry without clear guidance, it's not a minor inconvenience — it's a reliability failure. Every failed correction loop is a moment where your automation is stuck in a cycle of non-compliance. Gate Intelligence identifies the friction points that prevent agents from reaching a successful state, and feeds that knowledge back into the gate's contract so the next agent gets it right on the first try.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;When we launched Flow, the pitch was straightforward: define a gate with a schema and business rules, point your agent at it, and Flow validates the payload before it reaches your database. Schema checks, expression-based rules, optional human approval. It works well — agents submit data, gates validate it, failed submissions come back with structured errors the agent can act on.&lt;/p&gt;

&lt;p&gt;But we were sitting on a pile of useful data and not doing anything with it.&lt;/p&gt;

&lt;p&gt;Every run Flow processes is stored: the input payload, the validation verdict, which rules passed, which failed, and the exact values that caused the failure. For agents that self-correct, we track the full chain — first attempt, second attempt, third, until either the agent gets it right or gives up. That's tens of thousands of data points per gate per week, and until now, it only showed up as numbers on the analytics dashboard.&lt;/p&gt;

&lt;p&gt;Gate Intelligence turns that data into concrete suggestions for improving your gates.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem It Solves
&lt;/h2&gt;

&lt;p&gt;Here's a real pattern we saw in our own test gates. We set up an invoice validation gate with five business rules: amount must be positive, currency must be a 3-letter uppercase code, vendor can't be empty, line items must sum to the total, and there must be at least one line item. Standard stuff.&lt;/p&gt;

&lt;p&gt;When we ran agents against it, 40% of first attempts failed the currency format rule. The agents were submitting "usd" and "eur" instead of "USD" and "EUR". Another 25% failed the line items sum check — off by a fraction of a cent due to floating-point rounding. 15% of submissions omitted the vendor field entirely.&lt;/p&gt;

&lt;p&gt;None of these are schema problems. The schema says currency is a string, vendor is required, amount is a number. All correct. The issue is that the gate's documentation and rules don't give agents enough context to get it right on the first try. The currency rule says "must equal its own uppercase version" — technically precise, but a Claude or GPT model reading the MCP tool description doesn't know that means "must be uppercase ISO 4217."&lt;/p&gt;

&lt;p&gt;When agents fail and retry without that context, the automation isn't saving time — it's stuck. Each failed loop is a moment where your pipeline is spinning instead of producing results. If an agent needs five attempts and 45 seconds to pass a rule that a single well-placed hint would have fixed on the first try, the gate itself is the bottleneck.&lt;/p&gt;

&lt;p&gt;Gate Intelligence identifies these patterns automatically and tells you what to do about them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh85ef5c2lsqqxm04qg1j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh85ef5c2lsqqxm04qg1j.png" alt="The gate configurator showing the 4-step card layout (Details → Hints → Schema → Processing → Delivery)" width="800" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Computes
&lt;/h2&gt;

&lt;p&gt;Every hour, a background job runs for each active gate. It analyzes the last 7 days of runs and computes six metrics:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Per-rule failure rates&lt;/strong&gt; — what percentage of first-attempt submissions fail each rule, with trend direction compared to the previous 7-day window. If your "amount must be positive" rule went from 5% failure to 15%, that's flagged as trending up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common failure values&lt;/strong&gt; — the actual values agents submitted that caused failures. For the currency rule, this surfaces "usd", "eur", "gbp" as the top offenders. For a numeric rule, it might show 0, -1, or 99999.999. These values are what make suggestions actionable — instead of "rule X fails a lot," the system can say "agents are submitting lowercase currency codes."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Field omission rates&lt;/strong&gt; — how often required schema fields are missing from submissions. A 30% omission rate on the vendor field means agents don't realize it's required, or the field name isn't clear enough.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chain convergence&lt;/strong&gt; — of all the failed submissions that triggered a correction chain, what percentage eventually succeeded? If agents submit, fail, retry, fail again, and give up 70% of the time, that's a fundamental reliability problem. A 33% convergence rate doesn't just mean "some retries" — it means your automation succeeds less than a third of the time. For any system that's supposed to run autonomously, that's a non-starter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Average chain length and time-to-correction&lt;/strong&gt; — how many attempts does it take, and how long does the cycle last? Two attempts averaging 3 seconds is healthy. Five attempts averaging 45 seconds means the agent is struggling.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs2y5s8amzccw7w8v5msn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs2y5s8amzccw7w8v5msn.png" alt="The three analysis cards (Rule Failure Rates, Field Omissions, Self-Correction Chains) showing real data with trend arrows and color-coded metrics" width="800" height="253"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pattern Detection
&lt;/h2&gt;

&lt;p&gt;Raw metrics tell you &lt;em&gt;what&lt;/em&gt; is failing. Pattern detection tells you &lt;em&gt;why&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The system examines common failure values and classifies them into four fixable patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Case mismatch&lt;/strong&gt; — the submitted value is a lowercase version of what's expected. "usd" vs "USD", "active" vs "Active". This usually means the gate needs to either make the rule case-insensitive or add explicit guidance about expected casing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rounding tolerance&lt;/strong&gt; — a numeric value is within 1% of the expected threshold but fails because of floating-point precision. An amount of 99.999 failing an exact equality check where the expected sum is 100.00. The fix is usually adding a small tolerance to the rule.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Type coercion&lt;/strong&gt; — a string representation of a number where a number is expected. The string "42" instead of the number 42. Common with agents that serialize JSON from natural language.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Empty string&lt;/strong&gt; — an empty string where a non-empty value is expected. Distinct from a missing field — the agent knows the field exists but doesn't have a value for it.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These patterns feed into the suggestions. Instead of a generic "rule X fails 60% of the time," the suggestion says "the currency format rule fails 60% of the time — agents submit lowercase currency codes (usd, eur, gbp). Consider adding a note that currency must be uppercase ISO 4217."&lt;/p&gt;

&lt;h2&gt;
  
  
  Suggestions and the Intelligence Tab
&lt;/h2&gt;

&lt;p&gt;Each gate now has an Intelligence tab alongside Configuration and Performance. The tab shows a summary bar with insight counts by severity, per-rule failure rates with trend arrows, field omission rates, chain convergence metrics, and a health trend chart built from historical snapshots.&lt;/p&gt;

&lt;p&gt;Below the analysis cards, concrete suggestions appear as dismissable cards with three severity levels:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Severity&lt;/th&gt;
&lt;th&gt;Trigger&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Critical&lt;/td&gt;
&lt;td&gt;Rule fails &amp;gt;50% of first attempts&lt;/td&gt;
&lt;td&gt;"Currency format fails 60% — add format guidance"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Critical&lt;/td&gt;
&lt;td&gt;Chain convergence below 50%&lt;/td&gt;
&lt;td&gt;"Agents give up on this gate 70% of the time"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Warning&lt;/td&gt;
&lt;td&gt;Required field missing &amp;gt;30%&lt;/td&gt;
&lt;td&gt;"Vendor omitted in 38% of submissions"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Info&lt;/td&gt;
&lt;td&gt;Rule never fails (500+ runs)&lt;/td&gt;
&lt;td&gt;"This rule may be redundant — 0 failures in 600 runs"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Info&lt;/td&gt;
&lt;td&gt;All rules &amp;gt;95% success&lt;/td&gt;
&lt;td&gt;"Excellent validation performance"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each suggestion has three actions: &lt;strong&gt;Apply&lt;/strong&gt;, &lt;strong&gt;Dismiss&lt;/strong&gt;, and &lt;strong&gt;Snooze&lt;/strong&gt; (hide for 7 days).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Famz08cx7ymfgcbej1r0l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Famz08cx7ymfgcbej1r0l.png" alt="Close-up of a critical-severity suggestion card showing the Apply/Dismiss/Snooze action buttons and the severity coloring" width="800" height="133"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Version-Controlled Hints
&lt;/h2&gt;

&lt;p&gt;This is where the architecture gets interesting. The first version of "Apply" directly modified the gate's description field — injecting hint text like "Common mistakes: agents submit lowercase currency codes." It worked, but it was a bad design for three reasons.&lt;/p&gt;

&lt;p&gt;First, no version control. The description change bypassed the gate's draft/publish pipeline. In regulated environments — banking, healthcare, insurance — operators need to know exactly when and why a gate's contract changed. A direct write to the description is invisible in the version history.&lt;/p&gt;

&lt;p&gt;Second, no audit trail. If an agent's behavior shifts after someone clicks "Apply" (for better or worse), there's no correlation between the click and the behavior change.&lt;/p&gt;

&lt;p&gt;Third, no review step. The hint goes live immediately. If Gate Intelligence generates five suggestions and the operator clicks Apply on all of them, five changes hit production with no review.&lt;/p&gt;

&lt;p&gt;So we changed the approach. Hints are now a first-class versioned field on the gate — stored alongside the schema, business rules, and identity key fields. When you click "Apply" on a suggestion, it adds the hint text to the gate's &lt;strong&gt;draft&lt;/strong&gt; version. If no draft exists, it creates one. The hint doesn't go live until you review it in the gate configurator and publish.&lt;/p&gt;

&lt;p&gt;The gate configurator now has a dedicated "Hints" panel sitting between the Details and Schema steps — visible at a glance without opening any dialog. You can see what Intelligence suggested, edit the text, add your own custom hints, or remove ones you don't want. When you're satisfied, you publish the gate version — which goes through the existing audit log, resets circuit breakers, and notifies connected MCP sessions that the tool description has changed.&lt;/p&gt;

&lt;p&gt;This means hints get the same treatment as any other gate configuration change: versioned, auditable, rollbackable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjgmfetox5m6ejgel7br8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjgmfetox5m6ejgel7br8.png" alt="Hints Panel in the gate configurator, showing 2-3 hints between the Define Details and Schema &amp;amp; Validate step cards" width="800" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How Hints Reach the Agent
&lt;/h2&gt;

&lt;p&gt;The MCP tool description for each gate is assembled from three sources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Submit data to Invoice Validation
Validates invoice payloads before processing to the ERP system.

Business rules:
&lt;span class="p"&gt;-&lt;/span&gt; amount_positive: Amount must be greater than zero
&lt;span class="p"&gt;-&lt;/span&gt; currency_format: Currency must be valid ISO 4217
&lt;span class="p"&gt;-&lt;/span&gt; line_items_match: Line item totals must equal invoice amount

--- Best Practices ---
&lt;span class="p"&gt;-&lt;/span&gt; Currency must be uppercase ISO 4217 (e.g., USD, EUR, not usd)
&lt;span class="p"&gt;-&lt;/span&gt; Line item totals must sum to the invoice amount within ±0.01
&lt;span class="p"&gt;-&lt;/span&gt; Vendor name is required — do not submit an empty string
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The gate description is always included. Business rules are always appended so the agent knows the constraints. The "Best Practices" section only appears when the auto-hints toggle is enabled on the gate — it's off by default because it changes what agents see, and the gate owner should make that decision deliberately.&lt;/p&gt;

&lt;p&gt;The key improvement from the original architecture: reading hints is now a simple array read from the published gate record, not a database query against the insights table. The old approach queried the insights service every time an MCP tool description was built, which meant a database hit on every tool list request. The new approach reads directly from the gate record — the hints were copied there at publish time. This matters because MCP tool descriptions are assembled on every session connection and tool refresh. Moving from a database lookup to a direct read keeps the tool-build path fast and predictable, which is critical when you're serving multiple concurrent agent sessions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Historical Snapshots and Trend Analysis
&lt;/h2&gt;

&lt;p&gt;Each time the intelligence job runs, it saves a snapshot with aggregate metrics: total runs, overall failure rate, per-rule failure rates, field omission rates, chain convergence, and suggestion counts. This creates a time series of gate health that's visible in the Intelligence tab as a bar chart.&lt;/p&gt;

&lt;p&gt;The chart color-codes each bar: red for failure rates above 50%, amber for 20–50%, and the primary color below 20%. Hovering shows the exact values and date. Over time, you can see whether applying suggestions actually improved the gate's success rate — which is the whole point.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl05wqspg1b0kqj944d5y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl05wqspg1b0kqj944d5y.png" alt="The health trend chart showing failure rate bars over time, with visible improvement (red bars transitioning to amber/green) after hints were applied " width="800" height="162"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Gate Intelligence today is reactive — it analyzes historical data and suggests improvements. There are two directions on the roadmap:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Proactive schema evolution&lt;/strong&gt;: if Intelligence detects that agents consistently submit a field that isn't in the schema (say, a tax rate keeps appearing in payloads that only define amount and currency), it suggests adding it. This requires analyzing raw payloads beyond just validation results, which is a different data pipeline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI Judge integration&lt;/strong&gt;: the current business rules are deterministic expressions. We're building an "AI Judge" mode that evaluates payloads using an LLM for semantic checks that can't be expressed as expressions — things like "the description should be professional in tone" or "the address looks like a real postal address." Intelligence would track AI Judge pass/fail rates the same way it tracks expression rules, but the suggestion engine would need to account for the non-deterministic nature of LLM evaluation.&lt;/p&gt;

&lt;p&gt;Neither is shipped yet, but the foundation is designed for them. The analysis pipeline, suggestion engine, versioned hints, and snapshot time series are all extensible — adding a new data source feeds into the same pattern detection and suggestion framework without rearchitecting anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;If you have an active Flow gate with at least 50 runs, Intelligence will start generating insights on the next hourly cycle. Open any gate, click the Intelligence tab, and hit Refresh to trigger analysis immediately.&lt;/p&gt;

&lt;p&gt;We're rolling it out gradually — it's available today for all paid tiers (Starter, Growth, Scale) and will be available on the Free tier once we're confident in the compute overhead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftga8ggxgravyk9amibpk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftga8ggxgravyk9amibpk.png" alt="The full gate detail page showing all three tabs (Configuration, Performance, Intelligence) with the Intelligence tab active — gives readers the full picture of where this lives in the product" width="800" height="548"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Whether your agents are running on AWS Bedrock, OpenAI's API, or any other provider — the validation layer is where reliability is won or lost. If your gates are rejecting 60% of first attempts and your correction chains converge less than half the time, your automation isn't autonomous. It's just expensive retry logic. Gate Intelligence gives you the data to fix that, and the versioned hints to make the fix stick.&lt;/p&gt;

&lt;p&gt;Flow docs: &lt;a href="https://docs.rynko.dev/flow" rel="noopener noreferrer"&gt;docs.rynko.dev/flow&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Get started: &lt;a href="https://app.rynko.dev/signup" rel="noopener noreferrer"&gt;app.rynko.dev/signup&lt;/a&gt; — free tier, 500 runs/month, 3 gates, no credit card.&lt;/p&gt;

</description>
      <category>rynko</category>
      <category>ai</category>
      <category>agents</category>
      <category>automation</category>
    </item>
    <item>
      <title>How Rynko Flow Maps to the AWS Agentic AI Security Scoping Matrix</title>
      <dc:creator>Srijith Kartha</dc:creator>
      <pubDate>Thu, 12 Mar 2026 18:54:10 +0000</pubDate>
      <link>https://dev.to/srijith/how-rynko-flow-maps-to-the-aws-agentic-ai-security-scoping-matrix-f2k</link>
      <guid>https://dev.to/srijith/how-rynko-flow-maps-to-the-aws-agentic-ai-security-scoping-matrix-f2k</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;AWS published a framework for securing autonomous AI agents. We mapped every scope and security dimension to what Flow does today — and where the gaps are.&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;When AWS published the &lt;a href="https://aws.amazon.com/blogs/security/the-agentic-ai-security-scoping-matrix-a-framework-for-securing-autonomous-ai-systems/" rel="noopener noreferrer"&gt;Agentic AI Security Scoping Matrix&lt;/a&gt; in November 2025, it put language around something we'd been building toward with Rynko Flow for a few months. The framework categorizes agentic AI systems into four scopes based on two axes — agency (what the agent can do) and autonomy (how independently it acts) — and maps six security dimensions across each scope. It's been referenced by OWASP, CoSAI, and multiple systems integrators since publication.&lt;/p&gt;

&lt;p&gt;I read through it and realized we'd already implemented a significant portion of what it recommends, particularly at Scopes 2 through 4. But I also found gaps worth being honest about. This post walks through each scope, maps it to Flow's current capabilities, and flags where we're still building.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Quick Primer on What Flow Does
&lt;/h2&gt;

&lt;p&gt;For context if you haven't seen Flow before: Rynko Flow is a validation gateway that sits between AI agents and downstream systems. You define a gate with a JSON schema and business rules, your agent submits payloads to it, and Flow validates the data before it proceeds. Failed validations return structured errors the agent can use to self-correct. Successful validations return a tamper-proof &lt;code&gt;validation_id&lt;/code&gt;. Optionally, you can add human approval steps and webhook delivery.&lt;/p&gt;

&lt;p&gt;The pipeline:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwui9jufu1au9ciaq3v06.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwui9jufu1au9ciaq3v06.png" alt="Flow Gate Pipeline" width="800" height="106"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Gates are exposed as MCP tools, so agents discover and use them without per-gate integration code. We also support REST API submission for non-MCP agents.&lt;/p&gt;

&lt;p&gt;One distinction that matters throughout this post: &lt;strong&gt;Flow is a validation checkpoint, not a centralized orchestrator.&lt;/strong&gt; It doesn't manage agent workflows or decide what runs next. The agent (or whatever framework orchestrates it — LangGraph, CrewAI, your own code) decides when to call a gate and what to do with the result. Flow's job is narrower: validate the data, return a verdict, and track what happened.&lt;/p&gt;

&lt;p&gt;That said, Flow's webhook delivery does enable a form of event-driven orchestration — when a payload passes validation, the webhook can trigger the next agent or service in a pipeline, creating loosely-coupled handoffs without a central coordinator. This means Flow covers some of the AWS framework's security dimensions deeply (audit, agent controls, agency perimeters) and has a partial but real story for orchestration through webhooks.&lt;/p&gt;

&lt;p&gt;With that context, here's how Flow maps to each scope in the AWS framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scope 1: No Agency
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What AWS describes:&lt;/strong&gt; Systems with human-initiated processes and no autonomous change capabilities. The agent follows predefined paths, processes data within workflow nodes, but can't modify anything. Read-only operations. Fixed execution paths.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security focus:&lt;/strong&gt; Process integrity, boundary enforcement, preventing agents from exceeding their boundaries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How Flow maps here:&lt;/strong&gt; Flow isn't really designed for Scope 1. If your agent is purely read-only and follows a fixed workflow with no ability to produce output that reaches external systems, you don't need a validation gateway — there's nothing to validate.&lt;/p&gt;

&lt;p&gt;That said, Flow's schema validation does share DNA with one of Scope 1's key requirements: "input validation at each workflow step boundary." If you're building a pipeline where each stage processes data and hands it to the next, you could place a Flow gate between stages to validate that each node's output conforms to the expected shape. But that's using Flow as plumbing, not as an agent governance layer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flow coverage: Minimal — and that's fine.&lt;/strong&gt; Scope 1 agents don't produce autonomous outputs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scope 2: Prescribed Agency
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What AWS describes:&lt;/strong&gt; Human-initiated, human-approved agentic actions. Agents can gather information, analyze data, and prepare recommendations, but all actions of consequence require explicit human approval. This is the "human in the loop" (HITL) scope.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key characteristics:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Agents can execute change with human review and approval&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Real-time human oversight with approval workflows&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bidirectional interaction — agents can ask humans for context&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Audit trails of all human approval decisions&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Security focus:&lt;/strong&gt; Securing approval workflows, preventing agents from bypassing human authorization, and maintaining oversight effectiveness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How Flow maps here:&lt;/strong&gt; This is where Flow starts to fit well. The approval workflow was one of the first features we built into Flow, and it maps directly to what the AWS framework calls "approval gateway enforcement."&lt;/p&gt;

&lt;p&gt;Here's how each Scope 2 security dimension looks in Flow:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Security Dimension&lt;/th&gt;
&lt;th&gt;Scope 2 Requirement&lt;/th&gt;
&lt;th&gt;Flow Implementation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Identity context&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;User auth, service auth, human identity verification for approvals&lt;/td&gt;
&lt;td&gt;JWT auth for dashboard users, API key auth for agents (scoped to team/workspace), magic-link reviewer identity via HMAC-SHA256 signed tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data, memory, &amp;amp; state protection&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Role-based access control, human approval workflows, read-mostly permissions for agents&lt;/td&gt;
&lt;td&gt;Workspace-scoped gates, team-based RBAC, agents can only submit — they can't modify gate schemas or approve their own runs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Audit &amp;amp; logging&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Human decision audit trails, agent recommendation logging, approval process tracking&lt;/td&gt;
&lt;td&gt;Every run logged with full payload, per-rule validation verdicts, approval decisions (approve/reject) with reviewer identity and timestamp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agent &amp;amp; FM controls&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Approval gateway enforcement, extended session monitoring&lt;/td&gt;
&lt;td&gt;Gate validation = approval gateway. MCP session tracking with &lt;code&gt;mcpSessionId&lt;/code&gt; ties agent submissions to a specific session. Circuit breaker monitors session health&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agency perimeters &amp;amp; policies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Human-validated constraint changes, time-bound elevated access, multi-step validation&lt;/td&gt;
&lt;td&gt;Gate schema versioning (draft → publish cycle means a human approves schema changes). Magic links expire in 72 hours. Schema validation + business rules = multi-step validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Orchestration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multi-step workflow orchestration, approval-gated tool access, human-validated tool chains&lt;/td&gt;
&lt;td&gt;Flow doesn't centrally orchestrate — the agent or framework (LangGraph, CrewAI, etc.) decides what to call and when. However, Flow's webhook delivery provides an event-driven handoff mechanism: when a submission passes validation (and approval, if configured), the validated payload is pushed to a webhook endpoint that can trigger the next agent, tool, or service. This enables loosely-coupled pipeline orchestration without a central orchestrator — each gate validates one stage and hands off to the next via webhook&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The Scope 2 implementation consideration that stood out to me was "time-bounded approval tokens with automatic expiration." We built this: magic links for external reviewers are HMAC-SHA256 signed, expire in 72 hours, and are single-use for approval actions. The reviewer doesn't need a Rynko account — they click the link, see the payload rendered with safe content (sanitized HTML/Markdown), and approve or reject.&lt;/p&gt;

&lt;p&gt;One gap: the paper mentions "cryptographically signed approval decisions." Our approval decisions are stored in the database with reviewer identity and timestamp, but we don't produce a standalone cryptographic proof of the decision. It's an area where we could do more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flow coverage: Strong.&lt;/strong&gt; Approval workflows, audit trails, scoped identity, and time-bounded reviewer access align closely with Scope 2 requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scope 3: Supervised Agency
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What AWS describes:&lt;/strong&gt; Human-initiated, but the agent executes autonomously. The agent makes decisions and takes actions without further approval. Humans define objectives and trigger execution, but agents operate independently through dynamic planning and tool usage. Optional human intervention points exist, but the agent can proceed without them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key characteristics:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Agents can execute change with no (or optional) human review&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dynamic planning and decision-making during execution&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Direct access to external APIs and systems&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Persistent memory across extended sessions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Autonomous tool selection and orchestration within defined boundaries&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Security focus:&lt;/strong&gt; Maintaining control during autonomous execution, scope management, and behavioral monitoring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How Flow maps here:&lt;/strong&gt; This is Flow's primary operating mode. Most teams using Flow today have agents that submit data autonomously — no human in the loop — and rely entirely on the gate's schema and business rules to catch problems. The agent self-corrects from structured errors, and Flow's circuit breaker intervenes if the agent enters a failure loop.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Security Dimension&lt;/th&gt;
&lt;th&gt;Scope 3 Requirement&lt;/th&gt;
&lt;th&gt;Flow Implementation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Identity context&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Agent authentication, identity delegation for autonomous actions&lt;/td&gt;
&lt;td&gt;API keys authenticate agents at team/workspace scope. MCP sessions bind agent identity to a persistent session with its own state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data, memory, &amp;amp; state protection&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Context-aware authorization, just-in-time privilege elevation, dynamic permission boundaries&lt;/td&gt;
&lt;td&gt;Schema + business rules provide context-aware authorization — the gate evaluates each submission based on the data content, not just the caller's identity. Gate versioning allows operators to update what's accepted without downtime (publish a new version to tighten or relax rules). Flow doesn't provide privilege elevation — agents have the same permissions throughout a session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Audit &amp;amp; logging&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Comprehensive action logging, reasoning chain capture, extended session tracking&lt;/td&gt;
&lt;td&gt;Full run audit trail (payload, validation verdicts, processing time). Self-correction chain tracking links retries — you can see the full sequence of submit → fail → correct → resubmit as a single chain with a shared &lt;code&gt;correlationId&lt;/code&gt;. MCP session IDs track activity across an agent's full conversation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agent &amp;amp; FM controls&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Container isolation, long-running process management, tool invocation sandboxing&lt;/td&gt;
&lt;td&gt;Gate validation acts as a sandbox for agent outputs — the agent can call &lt;code&gt;validate_*&lt;/code&gt; tools, but every payload must pass through deterministic rules before it's accepted. Circuit breaker prevents runaway retry loops by pausing the gate after consecutive failures. Flow doesn't provide container isolation or manage the agent's process lifecycle — it only controls the validation boundary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agency perimeters &amp;amp; policies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Runtime constraint evaluation, resource scaling limits, automated safety checks&lt;/td&gt;
&lt;td&gt;Business rules are evaluated at runtime against each submission — constraints are enforced per-payload, not just at setup time. Monthly run quotas cap total throughput per team. Circuit breaker acts as an automated safety check, tripping after N consecutive failures. These are static limits though — they don't adjust dynamically based on agent behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Orchestration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dynamic tool orchestration, parallel execution paths, cross-system integration&lt;/td&gt;
&lt;td&gt;Flow isn't a centralized orchestrator — it doesn't decide what runs next. But it supports two forms of integration: (1) MCP tool discovery, where agents find gates dynamically, and (2) webhook delivery, where validated payloads are pushed to downstream endpoints that can trigger the next step in a pipeline. This enables event-driven, loosely-coupled orchestration — gate A validates and webhooks to service B, which processes and submits to gate C. The sequencing emerges from the webhook chain, not from a central coordinator&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ferlldgsfgjp202tpdljn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ferlldgsfgjp202tpdljn.png" alt="Autonomous Self-Correction in Action: Rynko Flow guides the agent through two policy violations to a successful, compliant tool execution." width="800" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The self-correction chain tracking deserves specific mention here. The AWS paper talks about "reasoning chain capture" — being able to see why an agent made the decisions it did. Flow's chain tracking gives you a concrete version of this for validation: when an agent submits to a gate, fails, reads the errors, and resubmits, the entire sequence is linked by a &lt;code&gt;correlationId&lt;/code&gt;. You can see exactly what the agent submitted each time, which rules it violated, what it fixed, and whether it eventually succeeded. In the webapp, chains are displayed as collapsible groups — the latest attempt shows as the primary row with a badge showing "3 attempts," and expanding reveals the full correction timeline.&lt;/p&gt;

&lt;p&gt;The circuit breaker is Flow's implementation of the "automated safety checks" requirement. When an agent keeps failing the same gate — tracked per session for MCP agents, per payload hash for REST agents — the circuit breaker trips after a configurable number of consecutive failures. The gate transitions to a paused state, the system sends in-app and email notifications to the gate creator, and all further submissions are blocked until the cooldown expires or a new gate version is published.&lt;/p&gt;

&lt;p&gt;Here's what makes the circuit breaker interesting from the AWS framework perspective: it's an example of &lt;strong&gt;graceful degradation&lt;/strong&gt;, which the paper calls out as a key architectural pattern. The paper says: "Design systems to automatically reduce autonomy levels when security events are detected." The circuit breaker does exactly this — when the agent can't produce valid output, Flow reduces the agent's effective autonomy by blocking further submissions and notifying the human operator.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flow coverage: Strong to comprehensive.&lt;/strong&gt; Self-correction chains, circuit breaker, runtime validation, MCP session tracking, and structured audit trails address most Scope 3 requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scope 4: Full Agency
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What AWS describes:&lt;/strong&gt; Fully autonomous AI that initiates its own activities based on environmental monitoring, learned patterns, or predefined conditions. No human triggers the agent — it operates continuously, makes independent decisions about when and how to act. The highest level of agency and risk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key characteristics:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Self-directed activity initiation based on environmental triggers&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Continuous operation with minimal human oversight&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;High to full degrees of autonomy in goal setting, planning, and execution&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dynamic interaction with multiple external systems and agents&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Capability for recursive self-improvement&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Security focus:&lt;/strong&gt; Continuous behavioral validation, enforcing agency boundaries, preventing capability drift, and maintaining organizational alignment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How Flow maps here:&lt;/strong&gt; Flow isn't a Scope 4 system itself — it doesn't initiate actions or make autonomous decisions. But it serves as a governance layer that Scope 4 agents submit to. The distinction matters: Flow doesn't control what the agent does; it controls what outputs the agent can successfully land in downstream systems. In the AWS framework's terms, Flow provides the "Advanced Deterministic Guardrails" that Scope 4 requires.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Security Dimension&lt;/th&gt;
&lt;th&gt;Scope 4 Requirement&lt;/th&gt;
&lt;th&gt;Flow Implementation&lt;/th&gt;
&lt;th&gt;Gap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Identity context&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dynamic identity lifecycle, federated auth, continuous identity verification, agent identity attestation&lt;/td&gt;
&lt;td&gt;API key auth, MCP session binding&lt;/td&gt;
&lt;td&gt;No agent identity attestation or dynamic identity lifecycle. Flow authenticates the agent but doesn't verify its internal state or attest to its identity to third parties&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data, memory, &amp;amp; state protection&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Behavioral authorization, adaptive access controls, continuous authorization validation&lt;/td&gt;
&lt;td&gt;Every submission is checked against schema and business rules — this provides continuous authorization validation at the data level. Gate versioning lets operators evolve rules over time. Business rules reject outputs that violate constraints regardless of which agent submitted them&lt;/td&gt;
&lt;td&gt;No ML-based adaptive controls — rules are deterministic, defined by humans. No behavioral authorization that learns from past patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Audit &amp;amp; logging&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Continuous behavioral logging, pattern analysis, predictive monitoring, automated incident correlation&lt;/td&gt;
&lt;td&gt;Full run audit trail. Chain tracking correlates related submissions. Circuit breaker events log failure patterns. Archive sync for long-term retention&lt;/td&gt;
&lt;td&gt;No predictive monitoring or ML-based pattern analysis. Circuit breaker counts failures but doesn't detect novel anomaly patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agent &amp;amp; FM controls&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Behavioral analysis, anomaly detection, automated containment, self-healing security&lt;/td&gt;
&lt;td&gt;Circuit breaker provides automated containment — pauses the gate and notifies operators when failures accumulate. Self-correction chain tracking gives visibility into agent retry patterns. Reset-on-publish clears stale circuit breaker state when new rules are deployed, which is closer to operational recovery than true self-healing&lt;/td&gt;
&lt;td&gt;No behavioral analysis beyond failure counting. No anomaly detection on payload content. No automated response that adapts without human intervention&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agency perimeters &amp;amp; policies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Self-adjusting boundaries, context-aware constraints, cross-system resource management, autonomous limit adaptation&lt;/td&gt;
&lt;td&gt;Business rules provide context-aware constraints (evaluated per-payload). Circuit breaker provides a form of autonomous limit adaptation — it auto-pauses the gate without human action. Monthly quotas cap resource usage&lt;/td&gt;
&lt;td&gt;No self-adjusting boundaries — rules are static until a human publishes a new version. No cross-system resource management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Orchestration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Autonomous multi-agent orchestration, cross-session learning, dynamic service discovery&lt;/td&gt;
&lt;td&gt;MCP tool discovery lets agents find gates dynamically. Webhook delivery enables event-driven handoffs between stages — a validated payload can trigger the next agent or service without a central orchestrator. This supports loosely-coupled multi-agent pipelines&lt;/td&gt;
&lt;td&gt;No centralized multi-agent coordination. No cross-session learning. No dynamic service discovery beyond MCP tool listing. Each gate is independent — Flow doesn't manage the pipeline topology&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I want to be transparent about where the gaps are, because the Scope 4 requirements are genuinely hard. The paper calls for "continuous monitoring with machine learning-based anomaly detection" and "automated response systems for behavioral deviations." Flow's circuit breaker is an automated response system, but it's simple — it counts consecutive failures. It doesn't analyze payload content for anomalies, detect drift in agent behavior patterns over time, or predict when an agent is likely to start producing invalid output.&lt;/p&gt;

&lt;p&gt;That said, Flow provides the infrastructure that makes Scope 4 deployment safer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Every submission is validated&lt;/strong&gt; — the agent can't skip the gate. Schema + business rules are deterministic, not probabilistic. A Scope 4 agent that submits an order with a negative total gets rejected regardless of how autonomously it's operating.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Self-correction is tracked&lt;/strong&gt; — you can see whether a Scope 4 agent is self-correcting successfully (resilient autonomy) or spiraling into repeated failures (failing autonomy). Chain tracking gives you this visibility without instrumenting the agent itself.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Automated containment&lt;/strong&gt; — the circuit breaker pauses the gate when failures accumulate. This is the "failsafe mechanism that can halt operations when confidence drops" that the paper recommends for Scope 4.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Human re-entry point&lt;/strong&gt; — when the circuit breaker trips, the gate creator gets notified (in-app + email). This is the "human ability to inject strategic guidance without disrupting operations" pattern. The human publishes a new gate version (potentially with adjusted rules), which reactivates the gate and resets circuit breakers.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Flow coverage: Partial but meaningful.&lt;/strong&gt; Flow provides the deterministic guardrails, automated containment, and audit infrastructure that Scope 4 requires. The gaps are in ML-based anomaly detection, agent identity attestation, and cross-session learning — areas that require capabilities beyond what a validation gateway provides on its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Six Security Dimensions — Summary Matrix
&lt;/h2&gt;

&lt;p&gt;Here's a consolidated view of how Flow maps across all four scopes and six dimensions:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Scope 1&lt;/th&gt;
&lt;th&gt;Scope 2&lt;/th&gt;
&lt;th&gt;Scope 3&lt;/th&gt;
&lt;th&gt;Scope 4&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Identity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;JWT + API key + magic links&lt;/td&gt;
&lt;td&gt;+ MCP session binding&lt;/td&gt;
&lt;td&gt;Gap: no agent attestation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data &amp;amp; state&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;RBAC, workspace scoping&lt;/td&gt;
&lt;td&gt;+ runtime schema validation, gate versioning&lt;/td&gt;
&lt;td&gt;Gap: no adaptive controls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Audit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Run logs, approval trails&lt;/td&gt;
&lt;td&gt;+ chain tracking, session tracking&lt;/td&gt;
&lt;td&gt;Gap: no predictive monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agent controls&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Approval gateway&lt;/td&gt;
&lt;td&gt;+ circuit breaker, chain tracking (observes correction, doesn't drive it)&lt;/td&gt;
&lt;td&gt;Gap: no behavioral analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agency perimeters&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Schema versioning, expiring tokens&lt;/td&gt;
&lt;td&gt;+ runtime rules, quotas, circuit breaker&lt;/td&gt;
&lt;td&gt;Gap: no self-adjusting boundaries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Orchestration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Webhook delivery enables event-driven handoffs&lt;/td&gt;
&lt;td&gt;+ MCP tool discovery&lt;/td&gt;
&lt;td&gt;Gap: no centralized orchestration or cross-session learning. Supports loosely-coupled pipelines via webhooks, not coordinated multi-agent workflows&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The Key Architectural Patterns
&lt;/h2&gt;

&lt;p&gt;The AWS paper concludes with five architectural patterns for agentic AI deployments. Flow aligns with four of them:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Progressive autonomy deployment&lt;/strong&gt; — "Start with Scope 1 or 2 implementations and gradually advance." Flow supports this directly. A gate can start with approval workflows (Scope 2 — human reviews every submission). Once you're confident in the schema and rules, remove the approval step and let the agent operate autonomously (Scope 3). The gate's validation logic stays the same; you're just adjusting the human oversight level.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Continuous validation loops&lt;/strong&gt; — "Establish automated systems that continuously verify agent behavior against expected patterns." This is literally what Flow does. Every agent submission is validated against the gate's schema and business rules. The self-correction loop (submit → fail → read errors → fix → resubmit) is a continuous validation loop operating at the individual submission level.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Human oversight integration&lt;/strong&gt; — "Maintain meaningful human oversight through strategic checkpoints, behavioral reporting, and manual override capabilities." Flow's approval workflows are the strategic checkpoints. Chain tracking and circuit breaker notifications are the behavioral reporting. Gate versioning and manual pause/resume are the manual override capabilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Graceful degradation&lt;/strong&gt; — "Design systems to automatically reduce autonomy levels when security events are detected." The circuit breaker does this: when an agent accumulates consecutive failures, the gate pauses, notifications fire, and the agent's effective autonomy drops to zero until a human intervenes or the cooldown expires. The paper specifically recommends systems that "automatically inject tighter restrictions such as requiring more HITL or reducing the actions an agent can take" — this is exactly what happens when a gate transitions from auto-approve to paused.&lt;/p&gt;

&lt;p&gt;The one pattern we don't cover well is &lt;strong&gt;layered security architecture&lt;/strong&gt; — "defense in depth with security controls at multiple levels." Flow operates at the application layer (validating agent outputs). It doesn't provide network-level controls, model-level guardrails, or infrastructure-level isolation. Teams building Scope 3-4 systems need Flow as one layer in a broader security stack, not as the only layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We're Building Next
&lt;/h2&gt;

&lt;p&gt;Reading the AWS framework confirmed some things on our roadmap and added others:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI Judge (semantic validation)&lt;/strong&gt; — Currently, Flow's validation is deterministic: JSON Schema types and expression-based business rules. For Scope 4 agents producing unstructured or semi-structured outputs, deterministic rules aren't always enough. We're building an LLM-based evaluation mode where a second model reviews the agent's output against criteria defined in natural language. This addresses the "continuous behavioral validation" gap at Scope 4.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Approval timeout enforcement&lt;/strong&gt; — Our approval workflow creates a &lt;code&gt;pending_approval&lt;/code&gt; state, but there's no automatic expiration. The paper's Scope 2 recommendation of "time-bounded approval tokens with automatic expiration" applies here. We're adding configurable timeout with auto-reject or auto-escalate behavior.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agent behavioral baselining&lt;/strong&gt; — The paper's Scope 4 requirements for "pattern analysis" and "predictive monitoring" are beyond what a simple failure counter provides. We're exploring tracking submission patterns per agent (payload shape, submission frequency, validation pass rate) and flagging deviations. Not ML-based yet, but statistical baselines that surface when an agent's behavior changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Flow Fits in Your Agentic Architecture
&lt;/h2&gt;

&lt;p&gt;Rynko Flow isn't a complete Scope 4 security stack — no single product is. But it provides the validation gateway, automated containment, and audit infrastructure that the AWS framework identifies as critical across Scopes 2-4.&lt;/p&gt;

&lt;p&gt;If you're building agents that produce data destined for production systems — orders, invoices, tickets, customer records — Flow gives you deterministic guardrails that work regardless of which model or framework your agent uses. The gate doesn't care whether the agent is a Claude tool-use loop, a LangGraph pipeline, or a custom orchestrator. It validates the output, tracks the correction chain, and trips the circuit breaker if things go sideways.&lt;/p&gt;

&lt;p&gt;The AWS framework is worth reading in full — it provides a structured way to think about where your agent sits on the agency/autonomy spectrum and what security controls you need at that level. And if you're at Scope 2 or above, a validation gateway isn't optional — it's one of the six critical security dimensions.&lt;/p&gt;

&lt;p&gt;Paper: &lt;a href="https://aws.amazon.com/blogs/security/the-agentic-ai-security-scoping-matrix-a-framework-for-securing-autonomous-ai-systems/" rel="noopener noreferrer"&gt;The Agentic AI Security Scoping Matrix&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Flow docs: &lt;a href="https://docs.rynko.dev/flow" rel="noopener noreferrer"&gt;docs.rynko.dev/flow&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Get started: &lt;a href="https://app.rynko.dev/signup" rel="noopener noreferrer"&gt;app.rynko.dev/signup&lt;/a&gt; — free tier, 500 runs/month, 3 gates, no credit card.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>ai</category>
      <category>security</category>
      <category>rynko</category>
    </item>
    <item>
      <title>Validating CrewAI Agent Outputs with Rynko Flow</title>
      <dc:creator>Srijith Kartha</dc:creator>
      <pubDate>Tue, 10 Mar 2026 14:41:45 +0000</pubDate>
      <link>https://dev.to/srijith/validating-crewai-agent-outputs-with-rynko-flow-3e13</link>
      <guid>https://dev.to/srijith/validating-crewai-agent-outputs-with-rynko-flow-3e13</guid>
      <description>&lt;p&gt;CrewAI's strength is that you define agents with roles, goals, and tools, and the framework handles the orchestration. An agent researches, another analyzes, a third writes the report. The problem shows up when the last agent in the chain produces the final output — a JSON payload that needs to be structurally valid, conform to business rules, and sometimes get human approval before it goes downstream.&lt;/p&gt;

&lt;p&gt;Most CrewAI tutorials skip this part. The output comes back as a string, maybe you parse it as JSON, and you hope it's correct. In production, that hope turns into bugs.&lt;/p&gt;

&lt;p&gt;I've been using Rynko Flow as the validation layer after CrewAI tasks. The agent does its work, the output goes through a Flow gate that checks schema and business rules, and only validated data moves forward. When validation fails, the error response is structured enough that the agent can fix itself and retry.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We're Building
&lt;/h2&gt;

&lt;p&gt;A CrewAI crew with two agents:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Order Processor&lt;/strong&gt; — Takes a natural language order request and extracts structured data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validator&lt;/strong&gt; — Submits the extracted data to a Rynko Flow gate, handles errors, and retries if needed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The validator agent uses a custom tool that wraps the Flow API, so it gets structured validation errors directly in its tool response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;crewai httpx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://app.rynko.dev/signup" rel="noopener noreferrer"&gt;Rynko account&lt;/a&gt; (free tier is fine)&lt;/li&gt;
&lt;li&gt;A Flow gate with your schema (&lt;a href="https://docs.rynko.dev/flow/getting-started" rel="noopener noreferrer"&gt;setup guide&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;An OpenAI API key (CrewAI's default LLM)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Flow Validation Tool
&lt;/h2&gt;

&lt;p&gt;CrewAI agents use tools — Python functions decorated with &lt;code&gt;@tool&lt;/code&gt;. Here's one that submits data to a Flow gate and returns the result in a format the LLM can reason about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;crewai.tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;

&lt;span class="n"&gt;RYNKO_BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RYNKO_BASE_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.rynko.dev/api&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;RYNKO_API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RYNKO_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;GATE_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FLOW_GATE_ID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nd"&gt;@tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;validate_order&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Validate an order payload against the Flow gate.
    Input must be a JSON string with fields: vendor (string),
    amount (number), currency (USD/EUR/GBP/INR), po_number (optional string).
    Returns validation result with status and any errors.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSONDecodeError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Invalid JSON: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;RYNKO_BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/flow/gates/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;GATE_ID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/runs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;payload&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;RYNKO_API_KEY&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;validation_failed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;details&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
        &lt;span class="n"&gt;error_lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;- &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;field&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rule_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;validation_failed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;errors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;error_lines&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Fix these errors and resubmit.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;run_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;runId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;validation_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;validation_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tool returns structured JSON in both success and failure cases. When validation fails, the error messages are specific enough — "currency must be one of: USD, EUR, GBP, INR" — that the LLM can fix the issue without guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining the Agents
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;crewai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;

&lt;span class="n"&gt;order_processor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Order Processor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;goal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Extract structured order data from customer requests accurately&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;backstory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are an order processing specialist. You extract vendor name, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;amount, currency, and PO number from natural language requests. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You output clean JSON with fields: vendor, amount, currency, po_number. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Currency must be a 3-letter code (USD, EUR, GBP, or INR).&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;allow_delegation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;order_validator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Order Validator&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;goal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Validate extracted orders against business rules and fix any issues&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;backstory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You validate order data by submitting it to the validation gateway. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;If validation fails, you read the error messages carefully, fix each &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;issue in the JSON, and resubmit. You keep trying until it passes or &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;you&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ve made 3 attempts. Always report the final validation status.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;validate_order&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;allow_delegation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The validator agent has the Flow tool and explicit instructions to read errors and retry. CrewAI agents follow their backstory closely, so the self-correction behavior comes from the backstory rather than from framework-level retry logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining the Tasks
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;crewai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;

&lt;span class="n"&gt;extract_task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Extract order data from this customer request:&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{user_request}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Output a JSON object with fields: vendor (string), amount (number), &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;currency (3-letter code: USD, EUR, GBP, or INR), po_number (string, optional). &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Output ONLY the JSON, nothing else.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;expected_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A JSON object with vendor, amount, currency, and optional po_number&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;order_processor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;validate_task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Take the order JSON from the previous task and validate it using the &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;validate_order tool. If validation fails, read the error messages, fix &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;the JSON, and call the tool again with corrected data. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Report the final run ID and validation status.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;expected_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Validation result with run ID and status (validated or failed)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;order_validator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;extract_task&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;context=[extract_task]&lt;/code&gt; tells CrewAI to pass the output of the extract task to the validator. The validator then takes that JSON and runs it through Flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the Crew
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;crewai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Crew&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Process&lt;/span&gt;

&lt;span class="n"&gt;crew&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Crew&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;agents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;order_processor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_validator&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;extract_task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validate_task&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sequential&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;crew&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;kickoff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_request&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;We need to process an order from Globex Corp for &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;twelve thousand five hundred dollars, PO number PO-2026-042&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Final Result ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What Happens at Runtime
&lt;/h2&gt;

&lt;p&gt;When you run this, the output shows the full agent reasoning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Order Processor] Extracting order data...
&amp;gt; {"vendor": "Globex Corp", "amount": 12500, "currency": "USD", "po_number": "PO-2026-042"}

[Order Validator] Validating order...
&amp;gt; Using tool: validate_order
&amp;gt; Tool result: {"success": true, "status": "validated", "run_id": "..."}

--- Final Result ---
Order validated successfully. Run ID: 550e8400-...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now here's the interesting case. Say the processor extracts &lt;code&gt;currency: "Dollars"&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Order Validator] Validating order...
&amp;gt; Using tool: validate_order
&amp;gt; Tool result: {"success": false, "errors": ["- currency: must be one of: USD, EUR, GBP, INR"]}

[Order Validator] The currency is invalid. Fixing to "USD" and resubmitting...
&amp;gt; Using tool: validate_order
&amp;gt; Tool result: {"success": true, "status": "validated", "run_id": "..."}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The validator reads the error, fixes the currency, and resubmits. One retry, no human involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Multiple Agents Writing to the Same Gate
&lt;/h2&gt;

&lt;p&gt;CrewAI shines when you have multiple specialized agents. In a more complex setup, you might have separate crews for different order types — one for domestic orders, one for international, one for recurring subscriptions. All three can validate against the same Flow gate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Different crews, same validation gate
&lt;/span&gt;&lt;span class="n"&gt;domestic_crew&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Crew&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;domestic_processor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;...)&lt;/span&gt;
&lt;span class="n"&gt;international_crew&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Crew&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;intl_processor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;...)&lt;/span&gt;
&lt;span class="n"&gt;subscription_crew&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Crew&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sub_processor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The gate enforces consistent validation regardless of which crew produced the data. If you change a business rule — say, increasing the minimum order amount from $10 to $50 — you update it once in the Flow dashboard and every crew picks it up immediately.&lt;/p&gt;

&lt;p&gt;Flow's analytics dashboard shows validation results by session, so you can see which crew or agent is producing the most errors and needs prompt tuning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Human Approval
&lt;/h2&gt;

&lt;p&gt;For high-value orders, configure the gate's approval mode to require human review. When the validator submits a $50,000 order, Flow holds it in a &lt;code&gt;review_required&lt;/code&gt; state instead of auto-approving. A reviewer gets an email, reviews the payload, and approves or rejects.&lt;/p&gt;

&lt;p&gt;Your CrewAI task can poll for the approval result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wait_for_approval&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wait_for_approval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Poll a Flow run until it reaches a terminal state.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;RYNKO_BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/flow/runs/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;run_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;RYNKO_API_KEY&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;approved&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rejected&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;completed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;delivered&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;run_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;run_id&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timeout&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;run_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;run_id&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using MCP Instead of REST
&lt;/h2&gt;

&lt;p&gt;If you prefer the agent to discover Flow gates dynamically through tool calling (rather than hardcoding the gate ID), you can connect CrewAI to Flow's MCP endpoint. Flow auto-generates a &lt;code&gt;validate_{gate_slug}&lt;/code&gt; tool for each active gate, and the tool schema includes field types and constraints so the LLM knows what to submit.&lt;/p&gt;

&lt;p&gt;This is useful when your agents work across multiple gates and need to pick the right one based on context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local Development Setup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create project&lt;/span&gt;
&lt;span class="nb"&gt;mkdir &lt;/span&gt;crewai-flow-demo &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;crewai-flow-demo
python &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate

&lt;span class="c"&gt;# Install&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;crewai httpx python-dotenv

&lt;span class="c"&gt;# Environment&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .env &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
OPENAI_API_KEY=sk-...
RYNKO_API_KEY=your_api_key_here
FLOW_GATE_ID=your_gate_id_here
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;main.py&lt;/code&gt; with the code above, add &lt;code&gt;from dotenv import load_dotenv; load_dotenv()&lt;/code&gt; at the top, and run with &lt;code&gt;python main.py&lt;/code&gt;. CrewAI's &lt;code&gt;verbose=True&lt;/code&gt; shows you the full agent reasoning — useful for debugging prompt issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full Working Example
&lt;/h2&gt;

&lt;p&gt;The complete code — agents, tools, tasks, &lt;code&gt;.env.example&lt;/code&gt;, and two test scenarios — is in our &lt;a href="https://github.com/rynko-dev/developer-resources/tree/main/examples/crewai-flow-validation" rel="noopener noreferrer"&gt;developer resources repo&lt;/a&gt;. Clone it, add your API keys, and run &lt;code&gt;python src/main.py&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Resources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.rynko.dev/flow" rel="noopener noreferrer"&gt;Rynko Flow documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.crewai.com/" rel="noopener noreferrer"&gt;CrewAI documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://app.rynko.dev/signup" rel="noopener noreferrer"&gt;Sign up for free&lt;/a&gt; — 500 Flow runs/month, no credit card&lt;/li&gt;
&lt;li&gt;&lt;a href="https://asciinema.org/a/824113" rel="noopener noreferrer"&gt;Self-correction demo (terminal recording)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>crewai</category>
      <category>ai</category>
      <category>rynko</category>
      <category>agents</category>
    </item>
    <item>
      <title>Adding Output Validation to Your LangGraph Agent with Rynko Flow</title>
      <dc:creator>Srijith Kartha</dc:creator>
      <pubDate>Tue, 10 Mar 2026 14:12:42 +0000</pubDate>
      <link>https://dev.to/srijith/adding-output-validation-to-your-langgraph-agent-with-rynko-flow-41mi</link>
      <guid>https://dev.to/srijith/adding-output-validation-to-your-langgraph-agent-with-rynko-flow-41mi</guid>
      <description>&lt;p&gt;&lt;strong&gt;Your LangGraph agent works great in demos. But in production, every node's output needs to be validated before the next node acts on it. Here's how to add a validation step without writing custom checking logic.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;LangGraph gives you fine-grained control over your agent's execution graph — you define nodes, edges, and conditional routing. But one thing that's missing from most LangGraph tutorials is what happens when a node produces bad data. The next node just receives it and either crashes or propagates the error downstream.&lt;/p&gt;

&lt;p&gt;I ran into this when building an order processing pipeline with LangGraph. The extraction node would occasionally produce negative amounts, invalid currencies, or missing fields. The downstream nodes — pricing, invoicing, fulfillment — would silently process the bad data. By the time someone noticed, the damage was already in the database.&lt;/p&gt;

&lt;p&gt;The typical fix is writing validation logic inside each node. That works, but it means every node carries its own schema checks, the validation rules are scattered across your codebase, and there's no central place to see what's failing and why.&lt;/p&gt;

&lt;p&gt;So I hooked up Rynko Flow as an external validation step in the graph. The agent extracts data, Flow validates it against a schema and business rules, and only if it passes does the pipeline continue. If it fails, the agent gets structured errors it can use to self-correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You'll Build
&lt;/h2&gt;

&lt;p&gt;A LangGraph agent with three nodes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Extract&lt;/strong&gt; — LLM extracts order data from a natural language request&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate&lt;/strong&gt; — Submits the extracted data to a Rynko Flow gate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process&lt;/strong&gt; — Handles the validated order (or routes back for correction)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The graph looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;extract → validate → process
              ↓ (if failed)
          extract (retry with error context)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;langgraph langchain-openai httpx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll also need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://app.rynko.dev/signup" rel="noopener noreferrer"&gt;Rynko account&lt;/a&gt; (free tier works)&lt;/li&gt;
&lt;li&gt;A Flow gate configured with your order schema&lt;/li&gt;
&lt;li&gt;An OpenAI API key (or any LangChain-compatible LLM)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting Up the Flow Gate
&lt;/h2&gt;

&lt;p&gt;Create a gate in the &lt;a href="https://app.rynko.dev/flow/gates" rel="noopener noreferrer"&gt;Flow dashboard&lt;/a&gt; with this schema:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Constraints&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;vendor&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;required, min 1 char&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amount&lt;/td&gt;
&lt;td&gt;number&lt;/td&gt;
&lt;td&gt;required, &amp;gt;= 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;currency&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;required, one of: USD, EUR, GBP, INR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;po_number&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;optional&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Add a business rule: &lt;code&gt;amount &amp;gt;= 10&lt;/code&gt; with error message "Order amount must be at least $10."&lt;/p&gt;

&lt;p&gt;If you already have a Pydantic model, you can import the schema directly — run &lt;code&gt;YourModel.model_json_schema()&lt;/code&gt; and paste the output into the gate's Import Schema dialog. There's a &lt;a href="https://docs.rynko.dev/tutorials/import-pydantic-zod" rel="noopener noreferrer"&gt;tutorial for that&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Save and publish the gate. Note the gate ID — you'll need it in the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Validation Client
&lt;/h2&gt;

&lt;p&gt;First, a small wrapper around the Flow API. This is what the validate node will call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;RYNKO_BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RYNKO_BASE_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.rynko.dev/api&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;RYNKO_API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RYNKO_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_with_flow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gate_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Submit a payload to a Flow gate and return the result.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;RYNKO_BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/flow/gates/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gate_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/runs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;payload&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;RYNKO_API_KEY&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns the full validation result — status, errors, validation ID, the works. The important fields are &lt;code&gt;status&lt;/code&gt; (either &lt;code&gt;"validated"&lt;/code&gt; or &lt;code&gt;"validation_failed"&lt;/code&gt;) and &lt;code&gt;errors&lt;/code&gt; (an array of specific field-level issues when validation fails).&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining the Graph State
&lt;/h2&gt;

&lt;p&gt;LangGraph uses a typed state that flows between nodes. Ours tracks the user request, extracted data, validation result, and retry count:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TypedDict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TypedDict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;user_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;extracted_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;validation_result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;validation_errors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;retry_count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;final_result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Three Nodes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Extract Node
&lt;/h3&gt;

&lt;p&gt;The LLM extracts structured order data from the user's natural language request. If there were previous validation errors, they're included in the prompt so the LLM can correct its output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;GATE_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FLOW_GATE_ID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# Your gate ID
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;extract_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;OrderState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;error_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;validation_errors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;error_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;Your previous extraction had validation errors:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;validation_errors&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Fix these issues in your new extraction.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Extract order data from this request as JSON with fields: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vendor (string), amount (number), currency (string, one of USD/EUR/GBP/INR), &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;po_number (string, optional).&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Request: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;user_request&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;error_context&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Respond with ONLY valid JSON, no markdown.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;extracted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSONDecodeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;extracted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vendor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;amount&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;currency&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;extracted_data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;extracted&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Validate Node
&lt;/h3&gt;

&lt;p&gt;This is the Flow integration — submit the extracted data to the gate and capture the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;OrderState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;validate_with_flow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GATE_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;extracted_data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;validation_failed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;details&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
        &lt;span class="n"&gt;error_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;- &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;field&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rule_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;invalid&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;validation_result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;validation_errors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;error_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;retry_count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;retry_count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;validation_result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;validation_errors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Process Node
&lt;/h3&gt;

&lt;p&gt;If validation passed, the order moves forward. In a real system this would write to your database, trigger fulfillment, or call another API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;OrderState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;validation_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;validation_result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;validation_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;final_result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Order processed successfully.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Vendor: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;extracted_data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;vendor&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Amount: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;extracted_data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;amount&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;extracted_data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;currency&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Validation ID: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;validation_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;validation_id&lt;/code&gt; is a tamper-proof token from Flow — your downstream systems can verify that the data passed validation and hasn't been modified since.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wiring the Graph
&lt;/h2&gt;

&lt;p&gt;Now connect the nodes with conditional routing. If validation fails and we haven't exhausted retries, route back to the extract node with the error context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langgraph.graph&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StateGraph&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;END&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;should_retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;OrderState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;validation_errors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;retry_count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;retry&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;validation_errors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;give_up&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;proceed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Build the graph
&lt;/span&gt;&lt;span class="n"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StateGraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OrderState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;extract&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extract_order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;validate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validate_order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;process&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;process_order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_entry_point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;extract&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;extract&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;validate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_conditional_edges&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;validate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;should_retry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;retry&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;extract&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;# Back to extraction with error context
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;proceed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;process&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# Validation passed
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;give_up&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;END&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;         &lt;span class="c1"&gt;# Max retries reached
&lt;/span&gt;    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;process&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;END&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_request&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Process an order from Globex Corp for twelve thousand five hundred dollars USD, PO number PO-2026-042&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;retry_count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;final_result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order processed successfully.
Vendor: Globex Corp
Amount: 12500.0 USD
Validation ID: v_abc123...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Self-Correction Loop
&lt;/h2&gt;

&lt;p&gt;The interesting part is what happens when the LLM makes a mistake. Say it extracts &lt;code&gt;currency: "Dollars"&lt;/code&gt; instead of &lt;code&gt;"USD"&lt;/code&gt;. Flow returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"validation_failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"errors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"must be one of: USD, EUR, GBP, INR"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The graph routes back to the extract node, which now includes the error in its prompt. The LLM reads "currency must be one of: USD, EUR, GBP, INR", fixes its extraction to &lt;code&gt;"USD"&lt;/code&gt;, and the second attempt passes validation.&lt;/p&gt;

&lt;p&gt;This happens automatically — no human intervention, no hardcoded fixes. The LLM uses the structured error feedback from Flow to correct itself.&lt;/p&gt;

&lt;p&gt;In our testing, most validation issues resolve in one retry. The &lt;code&gt;retry_count&lt;/code&gt; cap of 3 is a safety net — if the agent can't fix it in three attempts, something is fundamentally wrong with the input and it's better to fail explicitly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Not Just Use Pydantic in the Node?
&lt;/h2&gt;

&lt;p&gt;You could validate with Pydantic directly in the extract node. For a single agent, that works fine. But Flow gives you a few things Pydantic doesn't:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Business rules that cross fields.&lt;/strong&gt; Pydantic validates field types and constraints, but expressions like &lt;code&gt;endDate &amp;gt; startDate&lt;/code&gt; or &lt;code&gt;quantity * price == total&lt;/code&gt; need custom validators. Flow evaluates these as expressions — you configure them in the dashboard, no code changes needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Centralized validation across agents.&lt;/strong&gt; If you have five different LangGraph pipelines submitting orders, they all validate against the same gate. Change a rule once, it applies everywhere. With Pydantic, you'd need to update the model in every repo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observability.&lt;/strong&gt; Flow's analytics dashboard shows you which fields fail most often, which business rules trigger, and which agents (by session) are producing the most errors. When you're debugging why Agent C keeps submitting bad currencies, this is where you look.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Approval workflows.&lt;/strong&gt; For high-value orders, add a human approval step on the gate. The pipeline pauses, a reviewer approves or rejects, and the graph resumes. You can't do this with a Pydantic validator.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding MCP for Direct Tool Access
&lt;/h2&gt;

&lt;p&gt;If you want the LLM to call Flow tools directly (instead of going through a hardcoded REST call), you can use LangChain's MCP tool integration. Flow's MCP endpoint at &lt;code&gt;https://api.rynko.dev/api/flow/mcp&lt;/code&gt; auto-generates a &lt;code&gt;validate_{gate_slug}&lt;/code&gt; tool for each active gate in your workspace.&lt;/p&gt;

&lt;p&gt;This means the LLM can discover available gates and submit payloads through tool calling, which is useful when the agent needs to decide which gate to validate against based on the input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local Development Setup
&lt;/h2&gt;

&lt;p&gt;To set up a local LangGraph development environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a project directory&lt;/span&gt;
&lt;span class="nb"&gt;mkdir &lt;/span&gt;langgraph-flow-demo &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;langgraph-flow-demo

&lt;span class="c"&gt;# Set up a virtual environment&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate

&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;langgraph langchain-openai httpx python-dotenv

&lt;span class="c"&gt;# Create .env file&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .env &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
OPENAI_API_KEY=sk-...
RYNKO_API_KEY=your_api_key_here
FLOW_GATE_ID=your_gate_id_here
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;main.py&lt;/code&gt; with the code from this tutorial, add &lt;code&gt;from dotenv import load_dotenv; load_dotenv()&lt;/code&gt; at the top, and run it with &lt;code&gt;python main.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For iterative development, LangGraph has a built-in visualization tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Print the graph structure
&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_graph&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;print_ascii&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Or save as PNG (requires pygraphviz)
&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_graph&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;draw_png&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;graph.png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shows you the nodes, edges, and conditional routing at a glance — useful for verifying the self-correction loop is wired correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full Working Example
&lt;/h2&gt;

&lt;p&gt;The complete code for this tutorial — including the graph, Flow client, &lt;code&gt;.env.example&lt;/code&gt;, and two test scenarios — is in our &lt;a href="https://github.com/rynko-dev/developer-resources/tree/main/examples/langgraph-flow-validation" rel="noopener noreferrer"&gt;developer resources repo&lt;/a&gt;. Clone it, add your API keys, and run &lt;code&gt;python src/main.py&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Resources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.rynko.dev/flow" rel="noopener noreferrer"&gt;Rynko Flow documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.rynko.dev/api-reference/flow" rel="noopener noreferrer"&gt;Flow API reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://langchain-ai.github.io/langgraph/" rel="noopener noreferrer"&gt;LangGraph documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://app.rynko.dev/signup" rel="noopener noreferrer"&gt;Sign up for free&lt;/a&gt; — 500 Flow runs/month, no credit card&lt;/li&gt;
&lt;li&gt;&lt;a href="https://asciinema.org/a/824113" rel="noopener noreferrer"&gt;Self-correction demo (terminal recording)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>langgraph</category>
      <category>ai</category>
      <category>python</category>
      <category>agents</category>
    </item>
    <item>
      <title>Launching Rynko Flow: A Self-Correcting Validation Gateway for AI Agent Outputs</title>
      <dc:creator>Srijith Kartha</dc:creator>
      <pubDate>Mon, 09 Mar 2026 17:41:18 +0000</pubDate>
      <link>https://dev.to/srijith/launching-rynko-flow-a-validation-gateway-for-ai-agent-outputs-cog</link>
      <guid>https://dev.to/srijith/launching-rynko-flow-a-validation-gateway-for-ai-agent-outputs-cog</guid>
      <description>&lt;p&gt;When we launched Rynko, the focus was document generation — templates, PDFs, Excel files. But the more we worked with teams building AI-powered workflows, the more we noticed the same problem showing up everywhere: the agent produces structured data, and the developer writes validation code to check it before passing it downstream. Schema checks, business rule enforcement, sometimes a human review step. Every team was building some version of this from scratch.&lt;/p&gt;

&lt;p&gt;So, we built Flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Flow Does
&lt;/h2&gt;

&lt;p&gt;Rynko Flow is a validation gateway that sits between your AI agent and your downstream systems. You define a gate with a schema and business rules, your agent submits payloads to it, and Flow validates the data before it moves forward. If the payload fails, the agent gets a clear error response it can act on. If it passes, Flow returns a tamper-proof &lt;code&gt;validation_id&lt;/code&gt; that downstream systems can verify to confirm the data hasn't been modified in transit.&lt;/p&gt;

&lt;p&gt;The pipeline looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0sh2wmmjo40kj9fflbws.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0sh2wmmjo40kj9fflbws.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each stage is independent. Schema validation checks field types, required fields, and constraints like min/max values and allowed enums. Business rules evaluate cross-field expressions — things like &lt;code&gt;endDate &amp;gt; startDate&lt;/code&gt; or &lt;code&gt;quantity * price == total&lt;/code&gt;. If you need a human to review before delivery, add an approval step with internal team members or external reviewers. Once everything passes, Flow delivers the payload to your webhook endpoints.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gates, Not Middleware
&lt;/h2&gt;

&lt;p&gt;A gate is a named validation checkpoint. It has a schema (the structure you expect), business rules (the constraints that cross fields), and optionally an approval configuration and delivery channels. Each gate gets its own API endpoint.&lt;/p&gt;

&lt;p&gt;Creating a gate takes about a minute in the dashboard:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Open the&lt;/strong&gt; &lt;a href="https://app.rynko.dev/flow/gates" rel="noopener noreferrer"&gt;&lt;strong&gt;Flow dashboard&lt;/strong&gt;&lt;/a&gt; and click &lt;strong&gt;Create Gate&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Name your gate&lt;/strong&gt; — give it something descriptive like "Order Validation". Flow generates a URL-friendly slug automatically (&lt;code&gt;order-validation&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Define the schema&lt;/strong&gt; — use the schema builder to add fields. For an order gate, you'd add &lt;code&gt;orderId&lt;/code&gt; (string, required), &lt;code&gt;amount&lt;/code&gt; (number, required, min 0), &lt;code&gt;currency&lt;/code&gt; (string, required, allowed values: USD/EUR/GBP), and &lt;code&gt;customerEmail&lt;/code&gt; (string, required, email format). Each field has a type dropdown and constraint options — no JSON to write by hand&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add business rules&lt;/strong&gt; — click &lt;strong&gt;Add Rule&lt;/strong&gt; and write expressions like &lt;code&gt;amount &amp;gt;= 10&lt;/code&gt; with an error message ("Order amount must be at least $10"). The rule editor validates your expression as you type, so you know it'll work before you save&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Save the gate&lt;/strong&gt; — it's immediately active and ready to receive payloads&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you already have your data models defined in code, you don't have to recreate the schema manually. Flow supports importing schemas directly from &lt;strong&gt;Pydantic&lt;/strong&gt; (Python) and &lt;strong&gt;Zod&lt;/strong&gt; (TypeScript). In the schema builder, click &lt;strong&gt;Import Schema&lt;/strong&gt;, pick the format, and paste the JSON Schema output from &lt;code&gt;model_json_schema()&lt;/code&gt; (Pydantic) or &lt;code&gt;zodToJsonSchema()&lt;/code&gt; (Zod). Flow maps the types, constraints, and required fields automatically. There's a &lt;a href="https://docs.rynko.dev/tutorials/import-pydantic-zod" rel="noopener noreferrer"&gt;full tutorial&lt;/a&gt; with code examples for both.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flum8zwfaulnvrbkl746h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flum8zwfaulnvrbkl746h.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This means if you have a Pydantic model like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ge&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;USD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;EUR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GBP&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;customer_email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EmailStr&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You run &lt;code&gt;Order.model_json_schema()&lt;/code&gt;, paste the output into the import dialog, and your gate schema is ready — field types, constraints, and all.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"order_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Order Id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Amount"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"minimum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Currency"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"enum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"EUR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GBP"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"customer_email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Customer Email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"order_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"customer_email"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;When your agent submits a payload to this gate, the response tells you exactly what happened at each validation layer:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"runId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"550e8400-e29b-41d4-a716-446655440000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"validated"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"validation_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"v_abc123..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"layers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pass"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"business_rules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pass"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If validation fails, the response includes specific error details — which field failed, which constraint was violated, which business rule returned false. The agent gets actionable feedback it can use to fix the data and resubmit.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why This Matters for AI Agents
&lt;/h2&gt;

&lt;p&gt;LLMs hallucinate. They produce plausible-looking data that might have an invalid enum value, a missing required field, or a number that violates a business constraint. When you're generating a single document, you catch these by eye. When an agent is processing hundreds of payloads autonomously, you need systematic validation.&lt;/p&gt;

&lt;p&gt;The interesting thing we've seen in practice is that agents self-correct. When an MCP-connected agent submits a payload that fails validation, it reads the error response, fixes the issues, and resubmits — often without any human involvement. We ran tests where we intentionally gave agents incomplete or incorrect data, and the validation-resubmission loop resolved the issues in one or two attempts. (ref: &lt;a href="https://docs.rynko.dev/reports/flow-mcp-agent-test" rel="noopener noreferrer"&gt;Flow MCP — AI Agent Integration Test Report | Rynko Documentation)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Flow has a built-in circuit breaker for this pattern. If an agent (identified by its MCP session) keeps submitting payloads that fail the same gate, Flow backs off — first warning, then temporarily blocking submissions from that session. This prevents a malfunctioning agent from burning through your quota with an infinite retry loop. The circuit breaker tracks failures per gate per session, with configurable thresholds and cooldown periods.&lt;/p&gt;
&lt;h2&gt;
  
  
  Multi-Agent Workflows
&lt;/h2&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;





&lt;p&gt;The single-agent case is straightforward, but Flow was really designed for multi-agent architectures — the kind you build with LangGraph, CrewAI, AutoGen, or your own orchestration layer. In these setups, you have multiple specialized agents handling different parts of a pipeline: one agent researches, another drafts, a third formats, and a fourth submits to your system of record. Each agent is good at its job, but none of them knows what the others are doing, and any of them can produce data that doesn't meet your downstream requirements.&lt;/p&gt;

&lt;p&gt;Gates are the shared contract between these agents and your systems. A "Customer Order" gate doesn't care whether the payload comes from a single monolithic agent or from the last step in a five-agent chain — it validates the same schema and business rules regardless. This means you can swap agents, change your orchestration graph, or add new agents to the pipeline without touching your validation logic. The gate is stable while the agents evolve around it.&lt;/p&gt;

&lt;p&gt;In practice, this plays out in a few ways:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pipeline validation.&lt;/strong&gt; An orchestrator runs Agent A (data extraction) → Agent B (enrichment) → Agent C (formatting), and the final output goes through a Flow gate before hitting your database. If Agent C produces bad data, the orchestrator gets structured errors it can route back to the responsible agent for correction — not a generic 400 from your API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parallel agents, same gate.&lt;/strong&gt; Multiple agents process different inputs concurrently — say, ten order-processing agents each handling a different customer. They all submit to the same "Order Validation" gate. Flow validates each independently, the circuit breaker tracks failures per session so one misbehaving agent doesn't affect the others, and your downstream system only receives validated payloads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-agent consistency.&lt;/strong&gt; When Agent A writes to the "Invoice" gate and Agent B writes to the "Payment" gate, and both gates have business rules referencing amount ranges and currency constraints, you get consistent validation across your entire agent fleet without encoding those rules in each agent's prompt.&lt;/p&gt;

&lt;p&gt;The analytics dashboard makes this observable — you can see which agents (by session) are hitting which gates, what their failure rates look like, and which business rules are triggering most often. When you're running dozens of agents in production, this is how you find the one that's drifting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Human-in-the-Loop When You Need It
&lt;/h2&gt;

&lt;p&gt;Not everything should be auto-approved. For high-value transactions, sensitive data changes, or any scenario where you want a human to verify before the data moves downstream, Flow supports approval workflows.&lt;/p&gt;

&lt;p&gt;You configure approvers on a gate — either team members who review from the dashboard, or external reviewers who get a magic link via email. External reviewers don't need a Rynko account. They click a link, see the payload, and approve or reject it. The magic links are HMAC-SHA256 signed, expire after 72 hours, and are single-use for approval actions.&lt;/p&gt;

&lt;p&gt;The approval model is any-approves: the first approver to act determines the outcome. For high-volume gates, we batch notification emails into 5-minute digests so reviewers don't get buried in individual emails. There's also a hard safety cap of 30 emails per hour per approver to prevent notification fatigue.&lt;/p&gt;

&lt;p&gt;The review experience for freetext content (Markdown, HTML, plain text) includes scroll-to-approve guardrails — the approve button stays disabled until the reviewer has scrolled through the entire content. For long documents, we auto-generate a table of contents from Markdown headers so reviewers can navigate quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  MCP Integration: Dynamic Tools per Gate
&lt;/h2&gt;

&lt;p&gt;This is where Flow connects directly to the AI agent workflow. When you connect an AI tool (Claude Desktop, Cursor, VS Code, or any MCP client) to Flow's MCP endpoint at &lt;code&gt;https://api.rynko.dev/api/flow/mcp&lt;/code&gt;, Flow auto-generates a validation tool for each active gate in your workspace.&lt;/p&gt;

&lt;p&gt;A gate with slug &lt;code&gt;order-validation&lt;/code&gt; becomes a tool called &lt;code&gt;validate_order_validation&lt;/code&gt;. The tool's input schema is generated from the gate's current schema — each field becomes a typed JSON Schema property with its constraints. The tool description includes the gate's business rule error messages, so the agent understands the constraints before submitting.&lt;/p&gt;

&lt;p&gt;When you update a gate's schema, Flow pushes a &lt;code&gt;notifications/tools/list_changed&lt;/code&gt; event to connected agents. They automatically see the updated tool list without reconnecting. No redeploy, no config change.&lt;/p&gt;

&lt;p&gt;A conversation with an MCP-connected agent might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You: Validate this order before submitting.

Agent: I'll validate it against your Order Validation gate.
       [calls validate_order_validation]

       The order passed validation:
       - Schema validation: pass
       - Business rules: pass
       - Validation ID: v_abc123...

       You can use the validation ID to confirm this data
       hasn't been modified when it reaches your order system.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Freetext Mode
&lt;/h2&gt;

&lt;p&gt;Not all agent outputs are structured JSON. Sometimes the output is a Markdown document, an HTML email body, or a code snippet. Flow supports a freetext mode where the gate accepts content as a string instead of a structured schema.&lt;/p&gt;

&lt;p&gt;Content format is declared at gate creation — plaintext, Markdown, HTML, or code. For Markdown and HTML content, Flow runs a sanitization pipeline on the backend using &lt;code&gt;sanitize-html&lt;/code&gt; with a strict allowlist. Script tags, iframes, event handlers, and inline styles are stripped. Links get &lt;code&gt;rel="noopener noreferrer"&lt;/code&gt;. The reviewer sees sanitized content in a sandboxed view.&lt;/p&gt;

&lt;p&gt;This is useful for agent workflows that produce reports, summaries, or email drafts where you need a human to review the content before it gets sent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Delivery and Reliability
&lt;/h2&gt;

&lt;p&gt;After validation (and approval, if configured), Flow delivers the payload to your webhook endpoints. Deliveries are signed with HMAC-SHA256 so you can verify the payload hasn't been tampered with.&lt;/p&gt;

&lt;p&gt;The retry policy is straightforward: 5 attempts with delays at 30 seconds, 2 minutes, and 10 minutes. Each delivery attempt is logged, and failed deliveries can be retried manually from the dashboard or via the SDK.&lt;/p&gt;

&lt;p&gt;Flow enforces per-team concurrency caps to keep the multi-tenant system fair — the exact limits scale with your tier, but even on the Scale plan, no single team can consume more than 25% of total worker concurrency. This prevents one team's spike from degrading service for everyone else.&lt;/p&gt;

&lt;h2&gt;
  
  
  SDKs
&lt;/h2&gt;

&lt;p&gt;We've added Flow support to all three official SDKs. The pattern is the same across Node.js, Python, and Java — submit a run, poll for result, handle approvals:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Rynko&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@rynko/sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Rynko&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RYNKO_API_KEY&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submitRun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gate_abc123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ORD-2026-042&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1250.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;customerEmail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;buyer@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForRun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;pollInterval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;approved&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Validated:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validatedPayload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;validation_failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Errors:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SDKs are at version 1.3.1 with 14 Flow methods covering gates (read-only), runs, approvals, and deliveries. All three SDKs include automatic retry with exponential backoff for rate limits and transient errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;p&gt;Flow is a separate subscription from Rynko's document generation (Render). The pricing is based on validation runs per month:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Runs/Month&lt;/th&gt;
&lt;th&gt;Gates&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Overage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Starter&lt;/td&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;Unlimited&lt;/td&gt;
&lt;td&gt;$29/mo&lt;/td&gt;
&lt;td&gt;$0.005/run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Growth&lt;/td&gt;
&lt;td&gt;100,000&lt;/td&gt;
&lt;td&gt;Unlimited&lt;/td&gt;
&lt;td&gt;$99/mo&lt;/td&gt;
&lt;td&gt;$0.004/run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scale&lt;/td&gt;
&lt;td&gt;500,000&lt;/td&gt;
&lt;td&gt;Unlimited&lt;/td&gt;
&lt;td&gt;$349/mo&lt;/td&gt;
&lt;td&gt;$0.002/run&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The free tier is gate-limited to 3 — this is intentional. Most teams find they need more gates once they start connecting multiple agents to different validation checkpoints, and that's the natural upgrade trigger. Paid tiers have unlimited gates.&lt;/p&gt;

&lt;p&gt;To celebrate the launch, we're opening a &lt;strong&gt;Founder's Preview&lt;/strong&gt; — &lt;a href="https://app.rynko.dev/signup" rel="noopener noreferrer"&gt;sign up today&lt;/a&gt; and get 3 months of the Growth tier (100,000 runs/month, unlimited gates) completely free. No credit card required, no commitment. Once the preview ends, you can stay on Growth or switch to any tier that fits your usage.&lt;/p&gt;

&lt;p&gt;If you also need document generation, Render Packs are available as add-ons on any tier — $19/month for 500 documents, $49 for 2,000, or $119 for 10,000.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Dashboard
&lt;/h2&gt;

&lt;p&gt;Flow comes with a full web dashboard for managing gates, reviewing runs, handling approvals, and tracking analytics. The gate configurator includes a visual schema builder (with Pydantic and Zod import), a business rule editor with live expression validation, and approval/delivery configuration. The runs view shows real-time status updates, validation error breakdowns, and a timeline of each run's journey through the pipeline.&lt;/p&gt;

&lt;p&gt;The analytics dashboard covers the metrics you'd expect — run outcomes by gate, top failing rules, approval rates and decision times, throughput over configurable periods, and circuit breaker health. These metrics help you tune your gates and catch systemic issues early.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://app.rynko.dev/signup" rel="noopener noreferrer"&gt;&lt;strong&gt;Sign up for free&lt;/strong&gt;&lt;/a&gt; — 500 Flow runs/month included, no credit card&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create a gate&lt;/strong&gt; in the &lt;a href="https://app.rynko.dev/flow/gates" rel="noopener noreferrer"&gt;Flow dashboard&lt;/a&gt; — define your schema and business rules&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Submit a test payload&lt;/strong&gt; using the &lt;a href="https://docs.rynko.dev/flow/getting-started" rel="noopener noreferrer"&gt;Quick Start guide&lt;/a&gt; or the dry-run endpoint (doesn't count against quota)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Connect your AI agent&lt;/strong&gt; via the &lt;a href="https://docs.rynko.dev/integrations/mcp-flow" rel="noopener noreferrer"&gt;MCP endpoint&lt;/a&gt; — Claude Desktop, Cursor, VS Code, Windsurf, Zed, or Claude Code&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Flow is live and production-ready. We've been running it internally for weeks and the architecture has handled sustained load without surprises. If you're building with AI agents and need a systematic way to validate their outputs before they reach downstream systems, this is what we built it for.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://app.rynko.dev/signup" rel="noopener noreferrer"&gt;Sign up&lt;/a&gt; | &lt;a href="https://docs.rynko.dev/flow" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt; | &lt;a href="https://docs.rynko.dev/integrations/mcp-flow" rel="noopener noreferrer"&gt;MCP Setup&lt;/a&gt; | &lt;a href="https://docs.rynko.dev/api-reference/flow" rel="noopener noreferrer"&gt;API Reference&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Questions or feedback:&lt;/em&gt; &lt;a href="mailto:support@rynko.dev"&gt;&lt;em&gt;support@rynko.dev&lt;/em&gt;&lt;/a&gt; &lt;em&gt;or&lt;/em&gt; &lt;a href="https://discord.gg/d8cU2MG6" rel="noopener noreferrer"&gt;&lt;em&gt;Discord&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>automation</category>
      <category>langchain</category>
    </item>
    <item>
      <title>Fast PDF Generation API: A Native Puppeteer Alternative</title>
      <dc:creator>Srijith Kartha</dc:creator>
      <pubDate>Mon, 23 Feb 2026 10:29:54 +0000</pubDate>
      <link>https://dev.to/srijith/fast-pdf-generation-api-a-native-puppeteer-alternative-43n1</link>
      <guid>https://dev.to/srijith/fast-pdf-generation-api-a-native-puppeteer-alternative-43n1</guid>
      <description>&lt;p&gt;Today, I am introducing Rynko. This is a new document generation platform built to help developers and AI agents design and generate PDF and Excel documents at scale without the traditional overhead.&lt;/p&gt;

&lt;p&gt;If you are building a SaaS, you eventually have to generate an invoice, a receipt, or a complex report. Developers usually waste days wrestling with CSS media queries or setting up resource-heavy HTML-to-PDF microservices using Puppeteer. Rynko provides the infrastructure to design, version, and generate documents deterministically. You can go from a blank canvas to a production-ready template in minutes and get back to building your core product.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture: Native Speed, No Bloat
&lt;/h3&gt;

&lt;p&gt;Rynko generates PDF and Excel documents from a single definition. This definition is a structured JSON component tree rather than HTML.&lt;/p&gt;

&lt;p&gt;We explicitly chose not to use HTML because headless browsers are heavy. A standard Chromium-based PDF generator can easily consume hundreds of megabytes of RAM per instance. Rynko uses a native layout pipeline powered by the Yoga Layout Engine and PDFKit.&lt;/p&gt;

&lt;p&gt;The result is a massive win for your server costs and performance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Low Footprint:&lt;/strong&gt; Rynko workers operate at roughly 50MB of memory.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;High Speed:&lt;/strong&gt; Documents generate in a median of 426 milliseconds.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deterministic:&lt;/strong&gt; Identical JSON input produces identical PDF output every single time. There are no rendering differences between your local machine and your production server.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Template Designer
&lt;/h3&gt;

&lt;p&gt;You do not have to write the JSON manually. Templates are designed visually using a drag-and-drop editor that supports over 19 component types. These include data tables, charts, dynamic QR codes, and conditional logic.&lt;/p&gt;

&lt;p&gt;Each component has a strict property schema validated at design time. You can preview templates in real time with live variable substitution and sample data. Once designed, the exact same template can generate both a highly styled PDF and a formatted Excel spreadsheet with native formulas.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI Integration: Let Agents Do the Work
&lt;/h3&gt;

&lt;p&gt;To make integration even faster, we built a native Model Context Protocol (MCP) server. This allows AI agents from Claude Desktop, Cursor, or Windsurf to interact with Rynko directly.&lt;/p&gt;

&lt;p&gt;You can prompt your IDE to "Generate an invoice template for Acme Corp with a tax calculated field." The agent will use the MCP tools to build the JSON tree and draft the template. You can then review it visually in the dashboard before using it in your application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Developer Experience
&lt;/h3&gt;

&lt;p&gt;We treat document generation as a code-first citizen. We provide official SDKs for Node.js, Python, and Java. These SDKs feature automatic retries with exponential backoff.&lt;/p&gt;

&lt;p&gt;You can batch generate multiple documents in a single API call. Final documents are delivered via cryptographically signed URLs that automatically expire after three days. Webhook deliveries include HMAC-SHA256 signature verification so you can securely update your database when a document is ready.&lt;/p&gt;

&lt;h3&gt;
  
  
  Infrastructure That Grows with You
&lt;/h3&gt;

&lt;p&gt;Rynko is easy enough for a weekend side project, but it is built to handle enterprise scale when your startup grows.&lt;/p&gt;

&lt;p&gt;We organize resources using Projects and Environments. You get complete resource isolation for your dev, staging, and production environments. When you land enterprise clients, Rynko is ready with PDF/A-2b compliance for long-term archival, role-based access control for your team, and full audit logs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Join the Public Beta: Founder's Preview
&lt;/h3&gt;

&lt;p&gt;Rynko isn't just a basic PDF wrapper. We are building the deterministic infrastructure that allows developers and AI agents to generate documents at scale.&lt;/p&gt;

&lt;p&gt;Rynko is currently in &lt;strong&gt;Public Beta: Founder's Preview&lt;/strong&gt;. Join today to claim &lt;strong&gt;5,000 free document generation credits&lt;/strong&gt; and start building deterministic document workflows without the Chromium overhead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://app.rynko.dev/signup" rel="noopener noreferrer"&gt;&lt;strong&gt;Try It Free&lt;/strong&gt;&lt;/a&gt; | &lt;a href="https://www.rynko.dev/mcp" rel="noopener noreferrer"&gt;&lt;strong&gt;MCP Setup Guide&lt;/strong&gt;&lt;/a&gt; | &lt;a href="https://docs.rynko.dev/" rel="noopener noreferrer"&gt;&lt;strong&gt;Documentation&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Questions? Join our&lt;/em&gt; &lt;a href="https://discord.gg/d8cU2MG6" rel="noopener noreferrer"&gt;&lt;strong&gt;&lt;em&gt;Discord&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt; &lt;em&gt;or check the&lt;/em&gt; &lt;a href="https://www.npmjs.com/package/@rynko/mcp-server" rel="noopener noreferrer"&gt;&lt;strong&gt;&lt;em&gt;npm package&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I ideate and draft content, then refine it with the aid of artificial intelligence tools like Claude and revise it to reflect my intended message.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>node</category>
      <category>api</category>
      <category>saas</category>
    </item>
  </channel>
</rss>
