<?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: Subhamoy Datta</title>
    <description>The latest articles on DEV Community by Subhamoy Datta (@itssubhamoy).</description>
    <link>https://dev.to/itssubhamoy</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4111444%2Fec33dcca-c22c-48f9-b9b9-0c4043577950.jpg</url>
      <title>DEV Community: Subhamoy Datta</title>
      <link>https://dev.to/itssubhamoy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/itssubhamoy"/>
    <language>en</language>
    <item>
      <title>Why LLMs Should Never Touch Money Directly: Building an AI Payment Recovery Agent Solo</title>
      <dc:creator>Subhamoy Datta</dc:creator>
      <pubDate>Sat, 05 Sep 2026 17:33:03 +0000</pubDate>
      <link>https://dev.to/itssubhamoy/why-llms-should-never-touch-money-directly-building-an-ai-payment-recovery-agent-solo-493o</link>
      <guid>https://dev.to/itssubhamoy/why-llms-should-never-touch-money-directly-building-an-ai-payment-recovery-agent-solo-493o</guid>
      <description>&lt;p&gt;&lt;em&gt;How I built REVA for Track 3 of the Razorpay AI Buildathon 2026—and why deterministic guardrails matter more than AI autonomy when money is involved.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem I Wanted to Solve
&lt;/h2&gt;

&lt;p&gt;Building for &lt;strong&gt;Track 3 of the Razorpay AI Buildathon 2026&lt;/strong&gt; as a solo developer came with one obvious challenge:&lt;/p&gt;

&lt;p&gt;There were a lot of talented engineers building impressive things.&lt;/p&gt;

&lt;p&gt;I didn't want to build another chatbot with an LLM sitting on top of an API.&lt;/p&gt;

&lt;p&gt;I wanted to build something where AI had to make &lt;strong&gt;real decisions&lt;/strong&gt;, while still operating within the constraints of a financial system.&lt;/p&gt;

&lt;p&gt;That led me to a problem I found particularly interesting:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What should happen after a payment fails?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A failed payment doesn't always mean lost revenue.&lt;/p&gt;

&lt;p&gt;Sometimes it's a temporary bank issue. Sometimes the card is expired. Sometimes the customer needs another payment method. And sometimes the payment may have actually succeeded even though the original request didn't return a definitive response.&lt;/p&gt;

&lt;p&gt;The difficult part isn't simply retrying.&lt;/p&gt;

&lt;p&gt;The difficult part is deciding &lt;strong&gt;when to retry, when not to retry, and when to involve a human—without accidentally charging the customer twice.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's what led me to build &lt;strong&gt;REVA — Razorpay Revenue Recovery Agent&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  1. The Problem: A Failed Payment Isn't Always Lost Revenue
&lt;/h1&gt;

&lt;p&gt;Payment failures can leave merchants with potentially recoverable revenue.&lt;/p&gt;

&lt;p&gt;The obvious solution seems simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retry the payment.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But blindly retrying every failed transaction creates another set of problems.&lt;/p&gt;

&lt;p&gt;A recovery system needs to understand &lt;em&gt;why&lt;/em&gt; the payment failed before deciding what to do next.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A temporary bank failure might justify a retry after a delay.&lt;/li&gt;
&lt;li&gt;An expired card should not be retried.&lt;/li&gt;
&lt;li&gt;A high-value order might deserve human intervention.&lt;/li&gt;
&lt;li&gt;A payment with uncertain settlement status should not be blindly retried.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most basic recovery systems approach this in one of two ways.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blind Retries
&lt;/h3&gt;

&lt;p&gt;Retry failed payments according to a fixed schedule.&lt;/p&gt;

&lt;p&gt;The system doesn't really understand the failure. It simply assumes that trying again is better than doing nothing.&lt;/p&gt;

&lt;p&gt;That can lead to unnecessary retries and, more importantly, duplicate-charge risks when payment state is uncertain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Static Rules
&lt;/h3&gt;

&lt;p&gt;Build a large collection of &lt;code&gt;if/else&lt;/code&gt; conditions.&lt;/p&gt;

&lt;p&gt;This is more predictable, but eventually becomes difficult to maintain and doesn't handle contextual decisions particularly well.&lt;/p&gt;

&lt;p&gt;I wanted something in between.&lt;/p&gt;

&lt;p&gt;An agent that could &lt;strong&gt;reason about the situation&lt;/strong&gt;, while a deterministic system made sure that the reasoning could never violate critical financial policies.&lt;/p&gt;




&lt;h1&gt;
  
  
  2. The Realization: AI Shouldn't Touch Money Directly
&lt;/h1&gt;

&lt;p&gt;My initial idea was straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Payment fails
      |
      v
Send error to LLM
      |
      v
LLM decides what to do
      |
      v
LLM retries payment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It sounded reasonable.&lt;/p&gt;

&lt;p&gt;Then I started thinking about failure scenarios.&lt;/p&gt;

&lt;p&gt;What happens if the model incorrectly interprets a payment failure?&lt;/p&gt;

&lt;p&gt;What if it decides to retry an expired card?&lt;/p&gt;

&lt;p&gt;What if the payment actually succeeded, but the response was delayed because of a network issue?&lt;/p&gt;

&lt;p&gt;What if the model decides to retry the same transaction twice?&lt;/p&gt;

&lt;p&gt;That's when the core design principle became clear:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;LLMs are probabilistic. Financial invariants cannot be.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A language model can produce a reasonable decision most of the time.&lt;/p&gt;

&lt;p&gt;A payment system cannot accept "most of the time."&lt;/p&gt;

&lt;p&gt;A single incorrect action can potentially result in a duplicate charge, an invalid retry, or a loss of customer trust.&lt;/p&gt;

&lt;p&gt;So instead of giving the AI direct access to payment execution, I separated &lt;strong&gt;reasoning from execution&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  3. The Brain-Gatekeeper Architecture
&lt;/h1&gt;

&lt;p&gt;The architecture became:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    Failed Payment
                          |
                          v
              +------------------------+
              |       THE BRAIN        |
              |       Gemini AI        |
              |                        |
              | Diagnose failure       |
              | Analyze context        |
              | Propose recovery plan  |
              +-----------+------------+
                          |
                   Proposed Action
                          |
                          v
              +------------------------+
              |     THE GATEKEEPER     |
              |     PolicyEngine       |
              |                        |
              | Idempotency checks     |
              | Retry limits           |
              | Card validity checks   |
              | Risk / LTV policies    |
              +-----------+------------+
                          |
                    Approved Action
                          |
                          v
              +------------------------+
              |    PAYMENT EXECUTOR    |
              |      Razorpay API      |
              +------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The separation is intentional.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Brain
&lt;/h2&gt;

&lt;p&gt;Gemini receives relevant transaction context, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Payment failure information&lt;/li&gt;
&lt;li&gt;Failure codes&lt;/li&gt;
&lt;li&gt;Customer history&lt;/li&gt;
&lt;li&gt;Transaction value&lt;/li&gt;
&lt;li&gt;Previous recovery attempts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It then proposes a recovery strategy.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Failure: Temporary bank failure
Attempts: 1
Order value: ₹4,999

Proposed action:
Retry after backoff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model is responsible for &lt;strong&gt;reasoning about context&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But its output is only a proposal.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Gatekeeper
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;PolicyEngine&lt;/code&gt; is deterministic TypeScript code.&lt;/p&gt;

&lt;p&gt;It doesn't care how convincing the AI's reasoning sounds.&lt;/p&gt;

&lt;p&gt;It checks whether the proposed action is actually allowed.&lt;/p&gt;

&lt;p&gt;Here's what an actual REVA transaction decision looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;──────────────────────────────────────────────────────────────────────────────
  TRANSACTION BREAKDOWN:
  ▸ [txn_10005]
    • Diagnosis:     Payment failed: expired_card
    • Strategy:      REQUEST_CUSTOMER_ACTION (Risk: MEDIUM)
    • Policy Guard:  APPROVED (Deterministic invariant check)
    • Settlement:    PENDING_WEBHOOK (₹0 claimed)
──────────────────────────────────────────────────────────────────────────────
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where the architecture becomes concrete.&lt;/p&gt;

&lt;p&gt;The AI identifies the failure as an &lt;code&gt;expired_card&lt;/code&gt; scenario and recommends &lt;code&gt;REQUEST_CUSTOMER_ACTION&lt;/code&gt; instead of blindly retrying the payment.&lt;/p&gt;

&lt;p&gt;The Gatekeeper then independently evaluates that proposed action against deterministic policies and approves it.&lt;/p&gt;

&lt;p&gt;Most importantly, the transaction remains in &lt;code&gt;PENDING_WEBHOOK&lt;/code&gt;, so &lt;strong&gt;₹0 is counted as recovered&lt;/strong&gt; until settlement is actually verified.&lt;/p&gt;

&lt;p&gt;This is the Brain-Gatekeeper principle in practice:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The AI reasons. The policy engine verifies. The payment system executes.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If any critical invariant fails:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;         |
         v

       BLOCKED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The AI can propose an action, but it cannot override the Gatekeeper.&lt;/p&gt;




&lt;h1&gt;
  
  
  4. Why This Architecture Matters
&lt;/h1&gt;

&lt;p&gt;This isn't simply about adding another validation layer.&lt;/p&gt;

&lt;p&gt;It's about separating two fundamentally different responsibilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI is good at:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Understanding unstructured information&lt;/li&gt;
&lt;li&gt;Reasoning over multiple signals&lt;/li&gt;
&lt;li&gt;Classifying failure scenarios&lt;/li&gt;
&lt;li&gt;Generating contextual recovery strategies&lt;/li&gt;
&lt;li&gt;Explaining why an action was proposed&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Deterministic code is good at:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Enforcing hard constraints&lt;/li&gt;
&lt;li&gt;Checking idempotency&lt;/li&gt;
&lt;li&gt;Enforcing retry limits&lt;/li&gt;
&lt;li&gt;Validating state transitions&lt;/li&gt;
&lt;li&gt;Blocking unsafe actions&lt;/li&gt;
&lt;li&gt;Guaranteeing predictable behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Trying to make one system do both creates unnecessary risk.&lt;/p&gt;

&lt;p&gt;So REVA treats the LLM as a &lt;strong&gt;reasoning engine&lt;/strong&gt;, not as the final authority.&lt;/p&gt;




&lt;h1&gt;
  
  
  5. The Engineering Battles
&lt;/h1&gt;

&lt;p&gt;Building the architecture was only the beginning.&lt;/p&gt;

&lt;p&gt;The real work started when I had to make the system behave correctly under failure conditions.&lt;/p&gt;

&lt;p&gt;Building solo meant there was nobody to pass the hard bugs to.&lt;/p&gt;

&lt;p&gt;Three challenges stood out.&lt;/p&gt;




&lt;h2&gt;
  
  
  Challenge 1: Making the Audit Ledger Resumable
&lt;/h2&gt;

&lt;p&gt;Financial workflows need reliable audit trails.&lt;/p&gt;

&lt;p&gt;I implemented an append-only audit ledger using JSONL records with a &lt;strong&gt;SHA-256 parent-hash chain&lt;/strong&gt;.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Record 1
   |
   +-- hash₁
         |
         v
Record 2
   |
   +-- parentHash = hash₁
   +-- hash₂
         |
         v
Record 3
   |
   +-- parentHash = hash₂
   +-- hash₃
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every record references the hash of the previous record.&lt;/p&gt;

&lt;p&gt;That gives us a tamper-evident chain.&lt;/p&gt;

&lt;p&gt;But during the early runs, I ran into a subtle problem.&lt;/p&gt;

&lt;p&gt;Whenever a new process started appending to an existing audit file, the hash chain broke.&lt;/p&gt;

&lt;p&gt;The new &lt;code&gt;AuditLogger&lt;/code&gt; instance was resetting to the genesis hash instead of continuing from the previous record.&lt;/p&gt;

&lt;p&gt;That meant the next entry couldn't correctly reference the existing chain.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Fix
&lt;/h3&gt;

&lt;p&gt;I updated the &lt;code&gt;AuditLogger&lt;/code&gt; constructor to inspect the existing ledger.&lt;/p&gt;

&lt;p&gt;If the file already exists, it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Reads the existing JSONL file.&lt;/li&gt;
&lt;li&gt;Locates the final JSON record.&lt;/li&gt;
&lt;li&gt;Extracts its &lt;code&gt;currentHash&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Uses that hash as the parent for the next record.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the chain can resume correctly across process restarts.&lt;/p&gt;

&lt;p&gt;This taught me an important lesson:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cryptographic integrity isn't only about hashing.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You also need reliable state recovery.&lt;/p&gt;




&lt;h1&gt;
  
  
  Challenge 2: Eliminating "Phantom Revenue"
&lt;/h1&gt;

&lt;p&gt;This became one of the most important design decisions in REVA.&lt;/p&gt;

&lt;p&gt;Many recovery systems are tempted to treat an API success response as recovered revenue.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API returns HTTP 200
        |
        v
₹5,000 recovered
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But that's not necessarily true.&lt;/p&gt;

&lt;p&gt;Payment state can change after the initial request.&lt;/p&gt;

&lt;p&gt;A transaction can still be pending, fail later, or require confirmation through a webhook.&lt;/p&gt;

&lt;p&gt;So I introduced a &lt;strong&gt;Zero-Assumption Settlement&lt;/strong&gt; state machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RECOVERY_ACTION
       |
       v
PENDING_WEBHOOK
       |
       |  ₹0 claimed
       |
       v
Verified Razorpay Webhook
       |
       v
SETTLED
       |
       v
Revenue Counted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important rule is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;An attempted recovery is not recovered revenue.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When an action is executed, the transaction moves to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PENDING_WEBHOOK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Recovered Revenue = ₹0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Revenue is only counted after an inbound Razorpay webhook arrives and its &lt;strong&gt;HMAC-SHA256 signature&lt;/strong&gt; is successfully verified.&lt;/p&gt;

&lt;p&gt;Only then can the transaction move to the settlement state.&lt;/p&gt;

&lt;p&gt;This may make the benchmark look less impressive than simply counting API successes.&lt;/p&gt;

&lt;p&gt;But it makes the metric much more honest.&lt;/p&gt;




&lt;h1&gt;
  
  
  Challenge 3: Taming API Rate Limits
&lt;/h1&gt;

&lt;p&gt;There was another practical problem.&lt;/p&gt;

&lt;p&gt;I wanted to evaluate REVA against a meaningful number of payment scenarios.&lt;/p&gt;

&lt;p&gt;But making an external LLM request for every transaction quickly runs into API rate limits.&lt;/p&gt;

&lt;p&gt;So I designed REVA with two execution modes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interactive LLM Mode
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;--mode&lt;/span&gt; llm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is designed for live operation and demonstrations.&lt;/p&gt;

&lt;p&gt;It provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real LLM reasoning&lt;/li&gt;
&lt;li&gt;Contextual recovery decisions&lt;/li&gt;
&lt;li&gt;Explainable AI output&lt;/li&gt;
&lt;li&gt;Interactive transaction analysis&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Deterministic Simulation Mode
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;--mode&lt;/span&gt; deterministic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is designed for large-scale evaluation.&lt;/p&gt;

&lt;p&gt;It provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reproducible simulations&lt;/li&gt;
&lt;li&gt;Seeded transaction generation&lt;/li&gt;
&lt;li&gt;Large-scale benchmarking&lt;/li&gt;
&lt;li&gt;No dependency on external LLM calls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The benchmark uses a deterministic seed so the same scenarios can be reproduced.&lt;/p&gt;

&lt;p&gt;This distinction was important to me.&lt;/p&gt;

&lt;p&gt;I didn't want to claim that I had made 1,000 live LLM calls when I hadn't.&lt;/p&gt;

&lt;p&gt;The benchmark and the live AI mode are deliberately separated.&lt;/p&gt;




&lt;h1&gt;
  
  
  6. The Benchmark: 1,000 Failed Payment Scenarios
&lt;/h1&gt;

&lt;p&gt;At this point, I had the architecture.&lt;/p&gt;

&lt;p&gt;But I still needed to answer the most important question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Does it actually work better?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I evaluated REVA against an empirical benchmark of &lt;strong&gt;1,000 simulated failed-payment scenarios&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The scenarios were modeled across &lt;strong&gt;9 real-world Indian payment failure categories&lt;/strong&gt;, using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;seed = 42
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Together, these scenarios represented approximately:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;₹24.85 Lakhs of at-risk revenue.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I compared three approaches:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A naive blind retry bot&lt;/li&gt;
&lt;li&gt;Static &lt;code&gt;if/else&lt;/code&gt; rules&lt;/li&gt;
&lt;li&gt;REVA's autonomous recovery agent&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The results were:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Strategy&lt;/th&gt;
&lt;th&gt;Recovery Rate&lt;/th&gt;
&lt;th&gt;Duplicate Charge Warnings&lt;/th&gt;
&lt;th&gt;Human Escalations&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Naive Blind Retry Bot&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;27.4%&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Static If/Else Rules&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;41.2%&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;REVA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;68.3%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;14&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The difference was significant.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blind Retry Bot
&lt;/h3&gt;

&lt;p&gt;The naive retry system recovered only:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;27.4%&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It also generated:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;19 duplicate-charge warnings&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The system was essentially following one rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Payment failed → try again.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That isn't enough for a financial recovery system.&lt;/p&gt;




&lt;h3&gt;
  
  
  Static Rules
&lt;/h3&gt;

&lt;p&gt;The static rules improved recovery to:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;41.2%&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And importantly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;0 duplicate-charge warnings&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This showed that deterministic safety rules work.&lt;/p&gt;

&lt;p&gt;But they still lacked the contextual reasoning needed to choose better recovery strategies.&lt;/p&gt;




&lt;h3&gt;
  
  
  REVA
&lt;/h3&gt;

&lt;p&gt;REVA reached:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;68.3% recovery&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;with:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;0 duplicate-charge warnings&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;and:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;14 intentional human escalations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The recovery improvement was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;+40.9 percentage points&lt;/strong&gt; over blind retries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;+27.1 percentage points&lt;/strong&gt; over static rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the 14 human escalations weren't treated as failures.&lt;/p&gt;

&lt;p&gt;They were part of the design.&lt;/p&gt;

&lt;p&gt;For certain high-value or high-risk transactions, the safest action isn't another autonomous attempt.&lt;/p&gt;

&lt;p&gt;It's:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Stop and ask a human.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  7. Why the Benchmark Is Important
&lt;/h1&gt;

&lt;p&gt;The recovery percentage is obviously interesting.&lt;/p&gt;

&lt;p&gt;But I think the methodology is more important.&lt;/p&gt;

&lt;p&gt;It would have been easy to make the benchmark look better.&lt;/p&gt;

&lt;p&gt;For example, I could have counted an HTTP success as recovered revenue.&lt;/p&gt;

&lt;p&gt;I didn't.&lt;/p&gt;

&lt;p&gt;I could have mixed live LLM calls with deterministic simulation and presented the entire thing as one benchmark.&lt;/p&gt;

&lt;p&gt;I didn't.&lt;/p&gt;

&lt;p&gt;I could have ignored duplicate-charge risks because the recovery percentage looked good.&lt;/p&gt;

&lt;p&gt;I didn't.&lt;/p&gt;

&lt;p&gt;Instead, REVA uses explicit states and deterministic simulation so that the results are reproducible and the definition of "recovered revenue" is clear.&lt;/p&gt;

&lt;p&gt;That matters when the system you're evaluating is dealing with money.&lt;/p&gt;




&lt;h1&gt;
  
  
  8. What Building REVA Solo Taught Me
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1. Guardrails Matter More Than Prompts
&lt;/h2&gt;

&lt;p&gt;It's easy to spend hours optimizing prompts.&lt;/p&gt;

&lt;p&gt;But in a financial agent, the more important question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What happens when the model is wrong?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer shouldn't be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Hopefully nothing."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It should be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Deterministic code blocks the unsafe action.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The best prompt in the world can't guarantee that an LLM will never produce an incorrect output.&lt;/p&gt;

&lt;p&gt;A policy engine can guarantee that certain outputs will never be executed.&lt;/p&gt;

&lt;p&gt;That distinction matters.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Autonomy Doesn't Mean Unlimited Authority
&lt;/h2&gt;

&lt;p&gt;Before building REVA, I associated agentic AI with giving an agent more tools and more freedom.&lt;/p&gt;

&lt;p&gt;This project changed that perspective.&lt;/p&gt;

&lt;p&gt;A useful agent doesn't necessarily need unlimited authority.&lt;/p&gt;

&lt;p&gt;REVA can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Diagnose failures&lt;/li&gt;
&lt;li&gt;Reason about transaction context&lt;/li&gt;
&lt;li&gt;Recommend strategies&lt;/li&gt;
&lt;li&gt;Prioritize recovery actions&lt;/li&gt;
&lt;li&gt;Decide when human intervention may be appropriate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But it cannot simply override financial policies.&lt;/p&gt;

&lt;p&gt;That's a much more useful definition of autonomy for high-stakes systems:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The agent can reason independently without being given unlimited execution authority.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3. Honest Metrics Are More Valuable Than Impressive Metrics
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;PENDING_WEBHOOK&lt;/code&gt; state was probably one of the most important decisions in the project.&lt;/p&gt;

&lt;p&gt;It would have been much easier to claim revenue as soon as a recovery API returned successfully.&lt;/p&gt;

&lt;p&gt;But that would create a misleading benchmark.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Action executed
      |
      v
PENDING_WEBHOOK
      |
      v
Verified settlement
      |
      v
Revenue counted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That makes the numbers more conservative.&lt;/p&gt;

&lt;p&gt;But it also makes them defensible.&lt;/p&gt;

&lt;p&gt;I'd rather have a smaller number that I can explain than a bigger number that depends on assumptions.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Building Solo Forces You to Understand the Whole System
&lt;/h2&gt;

&lt;p&gt;There was nobody else to hand the difficult parts to.&lt;/p&gt;

&lt;p&gt;I had to work through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Architecture&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;Payment workflows&lt;/li&gt;
&lt;li&gt;Agent reasoning&lt;/li&gt;
&lt;li&gt;Policy enforcement&lt;/li&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;li&gt;Audit logging&lt;/li&gt;
&lt;li&gt;State machines&lt;/li&gt;
&lt;li&gt;CLI design&lt;/li&gt;
&lt;li&gt;Testing&lt;/li&gt;
&lt;li&gt;Benchmarking&lt;/li&gt;
&lt;li&gt;API integration&lt;/li&gt;
&lt;li&gt;Failure handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It was exhausting.&lt;/p&gt;

&lt;p&gt;But it also meant that I couldn't hide behind abstractions.&lt;/p&gt;

&lt;p&gt;When something broke, I had to understand why.&lt;/p&gt;

&lt;p&gt;And that gave me a much deeper understanding of the system I was building.&lt;/p&gt;




&lt;h1&gt;
  
  
  9. Testing the System
&lt;/h1&gt;

&lt;p&gt;I also wanted the safety layer to be testable independently of the LLM.&lt;/p&gt;

&lt;p&gt;The integration suite currently has:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;17/17 passing tests on Bun.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This matters because the most important safety guarantees shouldn't depend on whether the LLM happens to produce the expected output during a test run.&lt;/p&gt;

&lt;p&gt;The deterministic components need deterministic tests.&lt;/p&gt;

&lt;p&gt;The goal isn't to test whether the model is always right.&lt;/p&gt;

&lt;p&gt;The goal is to test whether the system behaves safely &lt;strong&gt;even when the model isn't right&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's the boundary I wanted REVA to enforce.&lt;/p&gt;




&lt;h1&gt;
  
  
  10. The Final Architecture
&lt;/h1&gt;

&lt;p&gt;By the end, REVA became less about:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Put an LLM on payments."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And more about:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Build a safe boundary around an LLM that reasons about payments."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The final flow 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;                    AI
                     |
                   Reason
                     |
                     v
              Proposed Action
                     |
                     v
          Deterministic Policies
                     |
              +------+------+
              |             |
           BLOCKED       APPROVED
                            |
                            v
                      Payment API
                            |
                            v
                         Webhook
                            |
                            v
                         SETTLED
                            |
                            v
                    Revenue Counted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The LLM doesn't get to decide whether money moves.&lt;/p&gt;

&lt;p&gt;It gets to &lt;strong&gt;suggest what should happen next&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The deterministic layer decides whether that suggestion is safe.&lt;/p&gt;

&lt;p&gt;And the settlement system determines whether the money was actually recovered.&lt;/p&gt;




&lt;h1&gt;
  
  
  11. The Bigger Lesson
&lt;/h1&gt;

&lt;p&gt;The most interesting thing I learned from this project wasn't a specific Gemini technique.&lt;/p&gt;

&lt;p&gt;It wasn't a particular prompt.&lt;/p&gt;

&lt;p&gt;It wasn't even the recovery rate.&lt;/p&gt;

&lt;p&gt;It was the realization that &lt;strong&gt;AI autonomy and system authority don't have to be the same thing.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An AI agent can be highly autonomous in its reasoning while still operating inside strict boundaries.&lt;/p&gt;

&lt;p&gt;That gives us a useful design pattern for high-stakes AI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LLM
 |
 | Reason
 v
Proposed Action
 |
 | Validate
 v
Deterministic Policy
 |
 +---- BLOCK ----&amp;gt; Stop
 |
 +---- APPROVE --&amp;gt; Execute
                       |
                       v
                   Verify State
                       |
                       v
                  Count Outcome
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern can extend beyond payments.&lt;/p&gt;

&lt;p&gt;The same principle applies anywhere an AI system can make decisions with real-world consequences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Financial operations&lt;/li&gt;
&lt;li&gt;Infrastructure changes&lt;/li&gt;
&lt;li&gt;Security systems&lt;/li&gt;
&lt;li&gt;Healthcare workflows&lt;/li&gt;
&lt;li&gt;Customer account actions&lt;/li&gt;
&lt;li&gt;Automated commerce&lt;/li&gt;
&lt;li&gt;Production deployments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model can be the reasoning layer.&lt;/p&gt;

&lt;p&gt;But the system around it needs to control what that reasoning is allowed to do.&lt;/p&gt;




&lt;h1&gt;
  
  
  12. What's Next?
&lt;/h1&gt;

&lt;p&gt;REVA is currently a buildathon project, but there are several directions I'd like to explore further.&lt;/p&gt;

&lt;h3&gt;
  
  
  Better Failure Classification
&lt;/h3&gt;

&lt;p&gt;Use richer transaction and historical signals to distinguish between temporary, recoverable, and fundamentally unrecoverable failures.&lt;/p&gt;

&lt;h3&gt;
  
  
  Smarter Recovery Policies
&lt;/h3&gt;

&lt;p&gt;Instead of simply choosing between a small set of strategies, optimize recovery decisions based on historical outcomes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Better Risk Modeling
&lt;/h3&gt;

&lt;p&gt;Introduce stronger fraud and transaction-risk signals into the policy layer.&lt;/p&gt;

&lt;h3&gt;
  
  
  More Granular Human-in-the-Loop Decisions
&lt;/h3&gt;

&lt;p&gt;Instead of a simple escalation path, determine exactly when human intervention creates the most value.&lt;/p&gt;

&lt;h3&gt;
  
  
  Production-Grade Event Processing
&lt;/h3&gt;

&lt;p&gt;Move toward a more robust event-driven architecture for handling payment state changes and webhooks at scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deeper Observability
&lt;/h3&gt;

&lt;p&gt;Make every AI proposal, policy decision, state transition, and settlement event observable and explainable.&lt;/p&gt;

&lt;p&gt;The interesting problem isn't simply making the AI smarter.&lt;/p&gt;

&lt;p&gt;It's figuring out:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How much authority should an AI agent have—and how can we safely increase that authority over time?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  13. Final Takeaway
&lt;/h1&gt;

&lt;p&gt;Building REVA changed the way I think about AI agents.&lt;/p&gt;

&lt;p&gt;I started with a simple idea:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Let an LLM recover failed payments."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I ended up with a very different architecture:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Let the LLM reason about recovery, but never let it bypass deterministic financial invariants.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Brain proposes.&lt;/p&gt;

&lt;p&gt;The Gatekeeper verifies.&lt;/p&gt;

&lt;p&gt;The payment system executes.&lt;/p&gt;

&lt;p&gt;The webhook confirms.&lt;/p&gt;

&lt;p&gt;Only then do we count the money.&lt;/p&gt;

&lt;p&gt;And that's probably the biggest lesson I took away from building REVA:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;In high-stakes AI systems, the goal shouldn't be to make the model all-powerful.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The goal should be to make the model useful while making the consequences of being wrong controllable.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;TypeScript&lt;/code&gt; · &lt;code&gt;Bun&lt;/code&gt; · &lt;code&gt;Docker&lt;/code&gt; · &lt;code&gt;Google Gemini&lt;/code&gt; · &lt;code&gt;Razorpay APIs&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;Built for &lt;strong&gt;Track 3 of the Razorpay AI Buildathon 2026&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you're building AI agents for payments, finance, or other high-stakes domains, I'd love to hear how you're handling the boundary between model reasoning and deterministic execution.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>hackathon</category>
    </item>
  </channel>
</rss>
