<?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: Suvishaz Tech</title>
    <description>The latest articles on DEV Community by Suvishaz Tech (@suvishaz_tech_2026).</description>
    <link>https://dev.to/suvishaz_tech_2026</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%2F4231814%2F4664e0dd-83d5-4bb7-b6ff-23299818b007.png</url>
      <title>DEV Community: Suvishaz Tech</title>
      <link>https://dev.to/suvishaz_tech_2026</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/suvishaz_tech_2026"/>
    <language>en</language>
    <item>
      <title>Letting an LLM read your ERP safely: a read-only pattern with an approval queue</title>
      <dc:creator>Suvishaz Tech</dc:creator>
      <pubDate>Sat, 07 Nov 2026 10:15:56 +0000</pubDate>
      <link>https://dev.to/suvishaz_tech_2026/letting-an-llm-read-your-erp-safely-a-read-only-pattern-with-an-approval-queue-4lkk</link>
      <guid>https://dev.to/suvishaz_tech_2026/letting-an-llm-read-your-erp-safely-a-read-only-pattern-with-an-approval-queue-4lkk</guid>
      <description>&lt;p&gt;Most teams that try to put an LLM next to their ERP hit the same wall within a week: the demo is impressive, and then someone from finance asks, "So what stops it from changing a posted invoice?"&lt;/p&gt;

&lt;p&gt;That question is the right one. An ERP is the system of record. If a model can write to it freely, every hallucination becomes a ledger entry. If it can't touch it at all, the model is just a chatbot that guesses.&lt;/p&gt;

&lt;p&gt;Here is the pattern we use in practice: &lt;strong&gt;the model reads directly, and it only proposes writes. A person approves every change.&lt;/strong&gt; It sounds simple, and it is — that's the point.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Split the world into two lanes
&lt;/h2&gt;

&lt;p&gt;Treat reads and writes as two completely different systems, not two permissions on the same one.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Read lane:&lt;/strong&gt; the agent can query master data, open documents, stock, ledgers and status fields. Results go back to the model as context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write lane:&lt;/strong&gt; the agent can never call a write API. It can only create a &lt;em&gt;proposal&lt;/em&gt; — a structured record describing the change it wants — in a queue outside the ERP.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The most important design decision is that the write lane does not exist inside the agent's toolset. It is not a flag that says "don't write". The tool simply isn't there.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Make the read lane genuinely read-only
&lt;/h2&gt;

&lt;p&gt;"Read-only" should be enforced by the ERP, not by the prompt.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a dedicated technical user for the agent with display-only roles (no create/change authorisations on any business object).&lt;/li&gt;
&lt;li&gt;Expose reads through a thin service layer (OData, REST, or DB views) rather than letting the model compose raw SQL.&lt;/li&gt;
&lt;li&gt;Whitelist the entities and fields the agent can see. Salary tables, bank details and anything personal stay out by default.&lt;/li&gt;
&lt;li&gt;Log every query with the user question that triggered it. When someone asks "why did the agent say that?", you want the exact rows it saw.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your ERP can't express "display only" cleanly for some object, don't expose that object yet. Add it later.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Turn every write into a proposal
&lt;/h2&gt;

&lt;p&gt;When the agent decides something should change — release a blocked PO, update a delivery date, create a credit note — it emits a proposal instead:&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;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"update_delivery_date"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PO 4500012345 / item 10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"current"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-11-12"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"proposed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-11-19"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Vendor confirmation email dated 6 Nov says dispatch slipped one week"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"evidence"&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;"mail:8831"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"po_history:4500012345"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"requested_by"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"agent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"medium"&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;A few rules make this work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Show the before and after.&lt;/strong&gt; The approver should never have to open the ERP to understand the change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attach evidence.&lt;/strong&gt; Every proposal links to the documents or rows that justify it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One proposal, one change.&lt;/strong&gt; No bundles. Bundles get rubber-stamped.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. The approval queue is where the human sits
&lt;/h2&gt;

&lt;p&gt;The queue is a small app (or a channel in your existing tool) where a named person approves, edits or rejects each proposal. Only after approval does a separate, non-LLM service execute the change with its own, narrowly scoped credentials.&lt;/p&gt;

&lt;p&gt;That executor is boring on purpose: it validates the proposal against a schema, re-reads the current value to make sure nothing changed since the proposal was made, applies the change, and writes the result back to the queue.&lt;/p&gt;

&lt;p&gt;Approval rights follow your existing authorisation matrix. If a buyer can't change a price in the ERP today, they can't approve the agent's price change either.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Close the loop
&lt;/h2&gt;

&lt;p&gt;Rejections are data. Store the reason ("wrong vendor", "already handled", "policy says no") and feed it back into the agent's context for similar cases. Over a few weeks, the proposal quality goes up and the reject rate drops — and you have a written record of why.&lt;/p&gt;

&lt;p&gt;Track three numbers: proposals per week, approval rate, and median time-to-approve. If approval rate is low, the agent needs better context. If time-to-approve is high, the proposals aren't clear enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. What you get
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No migration.&lt;/strong&gt; The ERP stays the system of record. Nothing is rebuilt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auditability.&lt;/strong&gt; Every change has a question, evidence, a proposal and a named approver.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A safe ramp.&lt;/strong&gt; Start with read-only Q&amp;amp;A. Add proposals for one low-risk action. Expand only when the numbers say so.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where to start
&lt;/h2&gt;

&lt;p&gt;Pick one question your team asks the ERP ten times a day ("where is this order?", "why is this invoice blocked?"). Build the read lane for just that. Ship it. Then add the first proposal type.&lt;/p&gt;

&lt;p&gt;If you want to see what this looks like as a product, this is the approach behind &lt;a href="https://www.suvishaz.com/agents/erp-knowledge-agent" rel="noopener noreferrer"&gt;Suvishaz Tech's ERP Knowledge Agent&lt;/a&gt;. The pattern itself is vendor-neutral, though, and works on SAP, Oracle, Dynamics, Tally or a home-grown system.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Note: this post was drafted by an AI agent for Suvishaz Tech.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>erp</category>
      <category>llm</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
