<?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: Marcos Taira</title>
    <description>The latest articles on DEV Community by Marcos Taira (@marcostaira).</description>
    <link>https://dev.to/marcostaira</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%2F3828111%2F9efe83d0-55b3-473c-b600-e3dc4196e373.jpg</url>
      <title>DEV Community: Marcos Taira</title>
      <link>https://dev.to/marcostaira</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/marcostaira"/>
    <language>en</language>
    <item>
      <title>How I connected SAP Business One to WhatsApp using AI agents (open-source)</title>
      <dc:creator>Marcos Taira</dc:creator>
      <pubDate>Mon, 16 Mar 2026 21:23:04 +0000</pubDate>
      <link>https://dev.to/marcostaira/how-i-connected-sap-business-one-to-whatsapp-using-ai-agents-open-source-1210</link>
      <guid>https://dev.to/marcostaira/how-i-connected-sap-business-one-to-whatsapp-using-ai-agents-open-source-1210</guid>
      <description>&lt;h1&gt;
  
  
  I built an open-source agent runtime that connects AI to SAP B1 via WhatsApp
&lt;/h1&gt;

&lt;h2&gt;
  
  
  The problem I was solving
&lt;/h2&gt;

&lt;p&gt;I work as a SAP Business One consultant. Every day I watch managers open the SAP client, navigate through five screens, run a report, export to Excel, and send it on WhatsApp.&lt;/p&gt;

&lt;p&gt;The data was always there. The friction was the interface.&lt;/p&gt;

&lt;p&gt;So I built Capyra: an autonomous agent runtime that lives in WhatsApp and acts on business systems directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;Your team sends a WhatsApp message:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How many open orders do we have today?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Capyra queries SAP B1 via Service Layer REST API and replies in seconds:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"7 orders pending approval, total R$ 48,320.&lt;br&gt;
Oldest: order #4821 from Acme Corp, waiting 3 days.&lt;br&gt;
Want me to send a reminder to the approvers?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No dashboards. No reports. No IT ticket.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WhatsApp (Evolution API)
        ↓
Capyra Gateway (WebSocket)
        ↓
Agent Loop (Claude / GPT)
        ↓
Skills (SAP B1, HTTP, Postgres...)
        ↓
Three-layer Memory (pgvector)
        ↓
Immutable Event Store
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent loop is simple: perceive → plan → act. Each action is recorded as an immutable event. Write operations always require user confirmation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Skills system
&lt;/h2&gt;

&lt;p&gt;Skills are directories with two files:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SKILL.md&lt;/strong&gt; — instructions for the LLM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# SAP Business One Skill&lt;/span&gt;

&lt;span class="gu"&gt;## Available Tools&lt;/span&gt;
&lt;span class="gu"&gt;### sap_get_orders&lt;/span&gt;
Retrieve sales orders with filters...

&lt;span class="gu"&gt;## Behavior Rules&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Always confirm before write operations
&lt;span class="p"&gt;-&lt;/span&gt; Format currency as R$ X.XXX,XX
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;index.ts&lt;/strong&gt; — tool implementation:&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;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SapB1Skill&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;SkillExecutor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...]&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toolName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// your SAP B1 Service Layer calls here&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;Any developer can build and share a skill for any system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three-layer memory
&lt;/h2&gt;

&lt;p&gt;One of the most interesting parts of Capyra is how it handles memory:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Episodic&lt;/strong&gt; — last N messages per session. Simple context window.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Semantic&lt;/strong&gt; — business facts stored as vector embeddings (pgvector). When a message arrives, relevant facts are retrieved by similarity and injected into the prompt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Procedural&lt;/strong&gt; — learned preferences per workspace. The agent learns things like "this client always orders in multiples of 50" and stores them as key-value pairs with confidence scores.&lt;/p&gt;

&lt;h2&gt;
  
  
  Audit trail
&lt;/h2&gt;

&lt;p&gt;Every agent action is an immutable event in PostgreSQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;occurred_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;approved_by&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;agent_events&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;occurred_at&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- message_in        → user sent a message&lt;/span&gt;
&lt;span class="c1"&gt;-- decision          → LLM decided to call a tool&lt;/span&gt;
&lt;span class="c1"&gt;-- tool_call         → tool was called&lt;/span&gt;
&lt;span class="c1"&gt;-- approval_required → user confirmation requested&lt;/span&gt;
&lt;span class="c1"&gt;-- approved          → user confirmed&lt;/span&gt;
&lt;span class="c1"&gt;-- tool_result       → tool returned data&lt;/span&gt;
&lt;span class="c1"&gt;-- message_out       → agent responded&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is critical for enterprise use: you can replay exactly what the agent did and why.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker
&lt;/h2&gt;

&lt;p&gt;No Node.js required — just Docker.&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;-O&lt;/span&gt; https://raw.githubusercontent.com/marcostaira/capyra/main/docker-compose.hub.yml
curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://raw.githubusercontent.com/marcostaira/capyra/main/.env.example
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;span class="c"&gt;# edit .env with your values&lt;/span&gt;
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.hub.yml up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Images on Docker Hub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;marcostaira/capyra-gateway&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;marcostaira/capyra-whatsapp&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Option 1 — CLI (Node.js required):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @capyra/cli
capyra init my-company
&lt;span class="nb"&gt;cd &lt;/span&gt;my-company
npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option 2 — Docker only:&lt;/strong&gt;&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;-O&lt;/span&gt; https://raw.githubusercontent.com/marcostaira/capyra/main/docker-compose.hub.yml
&lt;span class="nb"&gt;cp &lt;/span&gt;docker-compose.hub.yml .env.example .env
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.hub.yml up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both options set up PostgreSQL, Gateway, WhatsApp channel and SAP B1 connector automatically.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Telegram and Slack channels&lt;/li&gt;
&lt;li&gt;Skills marketplace&lt;/li&gt;
&lt;li&gt;Agent-to-Agent (A2A) protocol&lt;/li&gt;
&lt;li&gt;Computer Use skill for systems without API&lt;/li&gt;
&lt;li&gt;Totvs / Omie connectors&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: github.com/marcostaira/capyra&lt;/li&gt;
&lt;li&gt;npm: npmjs.com/package/@capyra/cli&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Built in Brazil 🇧🇷 · MIT License · Capybara mascot 🦫&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>automation</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
