<?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: Sospeter Mong'are</title>
    <description>The latest articles on DEV Community by Sospeter Mong'are (@msnmongare).</description>
    <link>https://dev.to/msnmongare</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%2F117091%2F89122cee-2645-481e-b979-f96819dc9d1b.jpeg</url>
      <title>DEV Community: Sospeter Mong'are</title>
      <link>https://dev.to/msnmongare</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/msnmongare"/>
    <language>en</language>
    <item>
      <title>Installing and Testing Docker on WSL with MobaXterm on Windows Os</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Tue, 16 Jun 2026 10:14:19 +0000</pubDate>
      <link>https://dev.to/msnmongare/installing-and-testing-docker-on-wsl-with-mobaxterm-on-windows-os-ooj</link>
      <guid>https://dev.to/msnmongare/installing-and-testing-docker-on-wsl-with-mobaxterm-on-windows-os-ooj</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;If you are a developer on Windows who wants to use Docker without installing Docker Desktop, you are in the right place. In this guide, you will learn how to install Docker Engine inside WSL (Windows Subsystem for Linux) using MobaXterm, test it with a real Node.js application, and manage your containers like a pro — all from the command line.&lt;/p&gt;

&lt;p&gt;By the end of this article, you will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand what WSL and MobaXterm are and why they work great together&lt;/li&gt;
&lt;li&gt;Have Docker Engine installed and running inside Ubuntu on WSL&lt;/li&gt;
&lt;li&gt;Test Docker with the official hello-world image&lt;/li&gt;
&lt;li&gt;Build and run a real Node.js web app inside a Docker container&lt;/li&gt;
&lt;li&gt;Know how to start, stop, and clean up Docker containers&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Who is this guide for?&lt;/strong&gt; Developers who are comfortable with the command line but are new to Docker on WSL. No prior Docker experience required.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  1. What Are WSL and MobaXterm?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  WSL — Windows Subsystem for Linux
&lt;/h3&gt;

&lt;p&gt;WSL is a feature built into Windows 10 and 11 that lets you run a real Linux environment directly on your Windows machine — no virtual machine, no dual boot. You get a full Ubuntu terminal that shares your file system and network with Windows.&lt;/p&gt;

&lt;p&gt;Think of WSL as a thin Linux layer that sits underneath your Windows desktop. It is fast, lightweight, and perfect for running developer tools like Docker, Node.js, Python, and more.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://mobaxterm.mobatek.net/" rel="noopener noreferrer"&gt;MobaXterm — A Better Terminal for WSL&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://mobaxterm.mobatek.net/" rel="noopener noreferrer"&gt;MobaXterm &lt;/a&gt;is a powerful terminal application for Windows that includes built-in SSH, X11 forwarding, and — most importantly — direct access to your WSL sessions. It gives you a much better development experience than the default Windows Terminal for WSL work.&lt;/p&gt;

&lt;p&gt;When you open a WSL tab in MobaXterm, you are working inside your Ubuntu environment. That is where all our Docker commands will run.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Important Note About WSL and systemd:&lt;/strong&gt; By default, WSL does not use systemd (the Linux service manager). This means &lt;code&gt;sudo systemctl start docker&lt;/code&gt; may not work. Instead, we use &lt;code&gt;sudo service docker start&lt;/code&gt;. This guide uses the correct command throughout.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  2. Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we begin, make sure you have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Windows 10 (version 2004 or later) or Windows 11&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://learn.microsoft.com/en-us/windows/wsl/install" rel="noopener noreferrer"&gt;WSL 2 installed with Ubuntu&lt;/a&gt;&lt;/strong&gt; (Ubuntu 20.04 or 22.04 recommended)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://mobaxterm.mobatek.net/" rel="noopener noreferrer"&gt;MobaXterm installed&lt;/a&gt;&lt;/strong&gt; — download free from mobaxterm.mobatek.net&lt;/li&gt;
&lt;li&gt;A stable internet connection for downloading packages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To verify WSL is installed, open PowerShell and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wsl &lt;span class="nt"&gt;--list&lt;/span&gt; &lt;span class="nt"&gt;--verbose&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see your Ubuntu distro listed with Version 2. If not, run &lt;code&gt;wsl --set-version Ubuntu 2&lt;/code&gt; to upgrade.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Opening Your WSL Session in MobaXterm
&lt;/h2&gt;

&lt;p&gt;Launch MobaXterm and start a WSL session:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open MobaXterm from your Windows Start menu&lt;/li&gt;
&lt;li&gt;Click on &lt;strong&gt;Sessions → New Session&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;WSL&lt;/strong&gt; from the session types&lt;/li&gt;
&lt;li&gt;Choose your Ubuntu distribution from the dropdown&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;OK&lt;/strong&gt; — you now have a Linux terminal open!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your prompt should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sospeter@DESKTOP-ABC123:~&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All commands in this guide are run inside this MobaXterm WSL terminal.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Installing Docker Engine on WSL
&lt;/h2&gt;

&lt;p&gt;We will install Docker Engine using the official Docker apt repository. Follow each step carefully.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Add Docker's Official GPG Key
&lt;/h3&gt;

&lt;p&gt;First, update your package list and install the required dependencies:&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="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;ca-certificates curl &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create the directory for Docker's GPG key and download it:&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="nb"&gt;sudo install&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; 0755 &lt;span class="nt"&gt;-d&lt;/span&gt; /etc/apt/keyrings
&lt;span class="nb"&gt;sudo &lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://download.docker.com/linux/ubuntu/gpg &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; /etc/apt/keyrings/docker.asc
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;a+r /etc/apt/keyrings/docker.asc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Add the Docker Repository
&lt;/h3&gt;

&lt;p&gt;Now add Docker's official package repository to your Ubuntu apt sources:&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="nb"&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/docker.sources &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; /etc/os-release &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;UBUNTU_CODENAME&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;$VERSION_CODENAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;
Components: stable
Architectures: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;dpkg &lt;span class="nt"&gt;--print-architecture&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;
Signed-By: /etc/apt/keyrings/docker.asc
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Install Docker Packages
&lt;/h3&gt;

&lt;p&gt;Install Docker Engine along with the CLI, containerd runtime, and the Compose plugin:&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="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;docker-ce docker-ce-cli containerd.io &lt;span class="se"&gt;\&lt;/span&gt;
  docker-buildx-plugin docker-compose-plugin &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This may take a few minutes depending on your internet speed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Start Docker
&lt;/h3&gt;

&lt;p&gt;In WSL, we start Docker using the &lt;code&gt;service&lt;/code&gt; command:&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="nb"&gt;sudo &lt;/span&gt;service docker start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To verify Docker is running:&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="nb"&gt;sudo &lt;/span&gt;service docker status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see output that includes &lt;code&gt;Docker is running&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🔁 &lt;strong&gt;Auto-start tip:&lt;/strong&gt; Add &lt;code&gt;sudo service docker start &amp;gt; /dev/null 2&amp;gt;&amp;amp;1&lt;/code&gt; to your &lt;code&gt;~/.bashrc&lt;/code&gt; file so Docker starts automatically every time you open MobaXterm.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 5: Add Your User to the Docker Group (Optional but Recommended)
&lt;/h3&gt;

&lt;p&gt;By default, Docker commands require &lt;code&gt;sudo&lt;/code&gt;. To run Docker without it:&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="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker &lt;span class="nv"&gt;$USER&lt;/span&gt;

&lt;span class="c"&gt;# Apply the group change (or close and reopen your terminal)&lt;/span&gt;
newgrp docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Verifying Docker with hello-world
&lt;/h2&gt;

&lt;p&gt;Let us confirm Docker is working with the official test image:&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="nb"&gt;sudo &lt;/span&gt;docker run hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see output 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;Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from Docker Hub.
 3. The Docker daemon created a new container from that image.
 4. The Docker daemon streamed that output to the Docker client.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see that — congratulations! 🎉 Docker is installed and working correctly on your WSL.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Building and Running a Real Node.js App
&lt;/h2&gt;

&lt;p&gt;The hello-world image is a good sanity check, but let us go further and run a real web application inside Docker.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create a Project Folder
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; ~/docker-test &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ~/docker-test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Create the Application File
&lt;/h3&gt;

&lt;p&gt;Create a file called &lt;code&gt;app.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano app.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&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;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeHead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello from Docker on WSL! 🐳&lt;/span&gt;&lt;span class="se"&gt;\n&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;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;Server running on port 3000&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save and exit with &lt;code&gt;Ctrl + X&lt;/code&gt;, then &lt;code&gt;Y&lt;/code&gt;, then &lt;code&gt;Enter&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Create the Dockerfile
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Use the official Node.js Alpine image (small and fast)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine&lt;/span&gt;

&lt;span class="c"&gt;# Set the working directory inside the container&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Copy our application file into the container&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; app.js .&lt;/span&gt;

&lt;span class="c"&gt;# Tell Docker our app listens on port 3000&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;

&lt;span class="c"&gt;# Command to run when the container starts&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "app.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Build the Docker Image
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; wsl-test-app &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-t&lt;/code&gt; flag gives the image a name (tag). The &lt;code&gt;.&lt;/code&gt; tells Docker to look for the Dockerfile in the current directory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Run the Container
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 &lt;span class="nt"&gt;--name&lt;/span&gt; my-test-app wsl-test-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What each flag does:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flag&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run in the background (detached mode)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-p 3000:3000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Map port 3000 on your host to port 3000 in the container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--name my-test-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Give the container a friendly name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wsl-test-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The image name we just built&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 6: Test the Running App
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello from Docker on WSL! 🐳
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also open &lt;code&gt;http://localhost:3000&lt;/code&gt; in your Windows browser and see the same result!&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Managing Your Containers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;What It Does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker ps&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all running containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker ps -a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all containers including stopped ones&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker logs my-test-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;View logs from a container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker stop my-test-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop a running container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker start my-test-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start a stopped container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker rm my-test-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete a container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker images&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all images on your machine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker rmi wsl-test-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete an image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker exec -it &amp;lt;name&amp;gt; sh&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Open a shell inside a running container&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Cleaning Up Your Test App
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Stop the running container&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker stop my-test-app

&lt;span class="c"&gt;# Remove the container&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker &lt;span class="nb"&gt;rm &lt;/span&gt;my-test-app

&lt;span class="c"&gt;# (Optional) Remove the image&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker rmi wsl-test-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Stopping Docker Engine
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;service docker stop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  8. Troubleshooting Common Issues
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Cannot connect to the Docker daemon&lt;/strong&gt;&lt;br&gt;
Run &lt;code&gt;sudo service docker start&lt;/code&gt; to start the Docker service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permission denied running Docker&lt;/strong&gt;&lt;br&gt;
Either prefix commands with &lt;code&gt;sudo&lt;/code&gt;, or add yourself to the docker group (Section 4, Step 5).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Port already in use&lt;/strong&gt;&lt;br&gt;
Change the port mapping to &lt;code&gt;-p 3001:3000&lt;/code&gt; and visit &lt;code&gt;localhost:3001&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WSL using too much RAM&lt;/strong&gt;&lt;br&gt;
Create &lt;code&gt;C:\Users\YourName\.wslconfig&lt;/code&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[wsl2]&lt;/span&gt;
&lt;span class="py"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;4GB&lt;/span&gt;
&lt;span class="py"&gt;processors&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  9. Quick Reference Cheat Sheet
&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;# Docker service&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;service docker start        &lt;span class="c"&gt;# Start Docker&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;service docker stop         &lt;span class="c"&gt;# Stop Docker&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;service docker status       &lt;span class="c"&gt;# Check status&lt;/span&gt;

&lt;span class="c"&gt;# Images&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; name &lt;span class="nb"&gt;.&lt;/span&gt;      &lt;span class="c"&gt;# Build image from Dockerfile&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker images               &lt;span class="c"&gt;# List images&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker rmi image-name       &lt;span class="c"&gt;# Delete image&lt;/span&gt;

&lt;span class="c"&gt;# Containers&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 &lt;span class="nt"&gt;--name&lt;/span&gt; app image   &lt;span class="c"&gt;# Run container&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker ps                   &lt;span class="c"&gt;# See running containers&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker ps &lt;span class="nt"&gt;-a&lt;/span&gt;                &lt;span class="c"&gt;# See all containers&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker logs app             &lt;span class="c"&gt;# View logs&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker stop app             &lt;span class="c"&gt;# Stop container&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker &lt;span class="nb"&gt;rm &lt;/span&gt;app               &lt;span class="c"&gt;# Remove container&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; app sh      &lt;span class="c"&gt;# Shell into container&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;You have successfully:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Installed Docker Engine on WSL using the official apt repository&lt;/li&gt;
&lt;li&gt;✅ Started and verified Docker is running with the hello-world image&lt;/li&gt;
&lt;li&gt;✅ Built and deployed a real Node.js application inside a Docker container&lt;/li&gt;
&lt;li&gt;✅ Learned how to manage, stop, and clean up containers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Docker on WSL is a powerful setup that gives you all the benefits of Linux containers without leaving your Windows machine. As you grow more comfortable, explore Docker Compose for multi-container applications, volume mounts for persistent data, and container networking for microservices architectures.&lt;/p&gt;

&lt;p&gt;Happy coding and containerizing! 🐳&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Found this helpful? Share it with fellow developers. Follow me on &lt;a href="https://x.com/msnmongare" rel="noopener noreferrer"&gt;X&lt;/a&gt; for more backend, API, and DevOps content.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ubuntu</category>
      <category>linux</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to Install dbt with SQL Server on WSL (Windows Subsystem for Linux)</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Wed, 10 Jun 2026 13:31:41 +0000</pubDate>
      <link>https://dev.to/msnmongare/how-to-install-dbt-with-sql-server-on-wsl-windows-subsystem-for-linux-2m3e</link>
      <guid>https://dev.to/msnmongare/how-to-install-dbt-with-sql-server-on-wsl-windows-subsystem-for-linux-2m3e</guid>
      <description>&lt;p&gt;Note: &lt;em&gt;A beginner-friendly, step-by-step guide for data engineers on Windows&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What is dbt?
&lt;/h2&gt;

&lt;p&gt;dbt (data build tool) is an open-source transformation tool that lets you write SQL-based data models, test them, and document them — all from the command line. It sits on top of your database and transforms raw data into analytics-ready tables and views.&lt;/p&gt;

&lt;p&gt;If you're working with data pipelines, ELT workflows, or data warehouses, dbt is a tool you'll encounter regularly.&lt;/p&gt;




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

&lt;p&gt;Before starting, make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WSL 2 with Ubuntu 24.04&lt;/strong&gt; installed on your Windows machine&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python 3.12&lt;/strong&gt; available (&lt;code&gt;python3 --version&lt;/code&gt; to check)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A virtual environment&lt;/strong&gt; already created and activated (see the Airflow installation guide if you need to set one up)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access to a SQL Server instance&lt;/strong&gt; — remote, Azure SQL, or local&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1: Activate Your Virtual Environment
&lt;/h2&gt;

&lt;p&gt;Always work inside a virtual environment to keep your dependencies isolated:&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="nb"&gt;source&lt;/span&gt; ~/your-project-folder/env/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your prompt should show &lt;code&gt;(env)&lt;/code&gt; at the beginning confirming it's active.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Install dbt with the SQL Server Adapter
&lt;/h2&gt;

&lt;p&gt;The SQL Server adapter for dbt is maintained as &lt;code&gt;dbt-sqlserver&lt;/code&gt;. Installing it also pulls in &lt;code&gt;dbt-core&lt;/code&gt; automatically:&lt;br&gt;
&lt;/p&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;dbt-sqlserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the installation:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Confirm the adapter is installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip show dbt-sqlserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dbt-sqlserver&lt;/span&gt;
&lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.x.x&lt;/span&gt;
&lt;span class="na"&gt;Summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A Microsoft SQL Server adapter plugin for dbt&lt;/span&gt;
&lt;span class="na"&gt;Requires&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dbt-adapters, dbt-common, dbt-core, pyodbc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;dbt --version&lt;/code&gt; may not always list plugins explicitly in some environments — &lt;code&gt;pip show dbt-sqlserver&lt;/code&gt; is the reliable way to confirm the adapter is present.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 3: Install the Microsoft ODBC Driver for SQL Server
&lt;/h2&gt;

&lt;p&gt;dbt-sqlserver uses &lt;code&gt;pyodbc&lt;/code&gt; under the hood, which requires Microsoft's ODBC driver to physically connect to SQL Server. Run the following commands &lt;strong&gt;one at a time&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Command 1 — Download and register Microsoft's GPG signing key:&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;-fsSL&lt;/span&gt; https://packages.microsoft.com/keys/microsoft.asc | &lt;span class="nb"&gt;sudo &lt;/span&gt;gpg &lt;span class="nt"&gt;--dearmor&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /usr/share/keyrings/microsoft-prod.gpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Command 2 — Add Microsoft's package repository:&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 https://packages.microsoft.com/config/ubuntu/24.04/prod.list | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/mssql-release.list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Command 3 — Refresh apt to pick up the new repository:&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;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Command 4 — Install the ODBC driver:&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;&lt;span class="nb"&gt;sudo &lt;/span&gt;&lt;span class="nv"&gt;ACCEPT_EULA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Y apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; msodbcsql18 unixodbc-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Verify the driver is registered:&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;odbcinst &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"ODBC Driver 18 for SQL Server"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[ODBC Driver 18 for SQL Server]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Microsoft ODBC Driver 18 for SQL Server&lt;/span&gt;
&lt;span class="py"&gt;Driver&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/opt/microsoft/msodbcsql18/lib64/libmsodbcsql-18.6.so.2.1&lt;/span&gt;
&lt;span class="py"&gt;UsageCount&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This confirms the driver is installed and ready.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Clone Your dbt Project Repository
&lt;/h2&gt;

&lt;p&gt;If you have an existing dbt project in a Git repository, clone it:&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="nb"&gt;cd&lt;/span&gt; ~
git clone &amp;lt;YOUR_REPO_URL_HERE&amp;gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; &amp;lt;cloned-folder-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the repository is private, authenticate using a GitHub Personal Access Token (PAT):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://&amp;lt;your-github-username&amp;gt;:&amp;lt;your-PAT&amp;gt;@github.com/org/repo.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 5: Install dbt Project Dependencies
&lt;/h2&gt;

&lt;p&gt;Most dbt projects have a &lt;code&gt;packages.yml&lt;/code&gt; file that lists external dbt packages. Install them with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dbt deps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 6: Configure Your &lt;code&gt;profiles.yml&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;dbt connects to your database using a &lt;code&gt;profiles.yml&lt;/code&gt; file. By default it lives at &lt;code&gt;~/.dbt/profiles.yml&lt;/code&gt;, but some projects include it locally inside the repo folder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create the default profiles directory and file:&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;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.dbt
nano ~/.dbt/profiles.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste the following template and fill in your actual credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;your_project_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev&lt;/span&gt;
  &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;dev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sqlserver&lt;/span&gt;
      &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ODBC&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Driver&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;18&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;SQL&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Server"&lt;/span&gt;
      &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your_server_address&lt;/span&gt;        &lt;span class="c1"&gt;# e.g. 192.168.1.10 or myserver.database.windows.net&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1433&lt;/span&gt;
      &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your_database_name&lt;/span&gt;
      &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your_schema_name&lt;/span&gt;
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your_username&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your_password&lt;/span&gt;
      &lt;span class="na"&gt;encrypt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;trust_cert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;                   &lt;span class="c1"&gt;# set to false in production with a valid SSL cert&lt;/span&gt;
      &lt;span class="na"&gt;login_timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;                  &lt;span class="c1"&gt;# seconds before connection attempt times out&lt;/span&gt;

    &lt;span class="na"&gt;prod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sqlserver&lt;/span&gt;
      &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ODBC&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Driver&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;18&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;SQL&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Server"&lt;/span&gt;
      &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your_prod_server_address&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1433&lt;/span&gt;
      &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your_prod_database&lt;/span&gt;
      &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your_prod_schema&lt;/span&gt;
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your_prod_username&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your_prod_password&lt;/span&gt;
      &lt;span class="na"&gt;encrypt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;trust_cert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;login_timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; The top-level key (&lt;code&gt;your_project_name&lt;/code&gt;) must exactly match the &lt;code&gt;name:&lt;/code&gt; field inside the project's &lt;code&gt;dbt_project.yml&lt;/code&gt; file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Save and exit (&lt;code&gt;Ctrl+O&lt;/code&gt;, &lt;code&gt;Enter&lt;/code&gt;, &lt;code&gt;Ctrl+X&lt;/code&gt;).&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: Test the Connection
&lt;/h2&gt;

&lt;p&gt;Run dbt's built-in connection test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dbt debug &lt;span class="nt"&gt;--target&lt;/span&gt; dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A successful output 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;All checks passed!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see a &lt;code&gt;Login timeout expired&lt;/code&gt; error, check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your server address is correct and reachable&lt;/li&gt;
&lt;li&gt;Port 1433 is open on the server's firewall&lt;/li&gt;
&lt;li&gt;Your credentials are correct in &lt;code&gt;profiles.yml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;You're on the right network (VPN may be required for remote servers)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test if the server port is reachable from WSL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nc &lt;span class="nt"&gt;-zv&lt;/span&gt; your_server_address 1433
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 8: Run Your dbt Models
&lt;/h2&gt;

&lt;p&gt;Once the connection test passes, run a specific model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dbt run &lt;span class="nt"&gt;--select&lt;/span&gt; model_name &lt;span class="nt"&gt;--target&lt;/span&gt; dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run all models:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dbt run &lt;span class="nt"&gt;--target&lt;/span&gt; dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run models against production:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dbt run &lt;span class="nt"&gt;--select&lt;/span&gt; model_name &lt;span class="nt"&gt;--target&lt;/span&gt; prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List all available models in the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dbt &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;--target&lt;/span&gt; dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Other Database Adapters
&lt;/h2&gt;

&lt;p&gt;dbt supports many databases. Here are the install commands for the most common ones:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Database&lt;/th&gt;
&lt;th&gt;Install Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SQL Server / Azure SQL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install dbt-sqlserver&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install dbt-postgres&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BigQuery&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install dbt-bigquery&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snowflake&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install dbt-snowflake&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install dbt-mysql&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redshift&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install dbt-redshift&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DuckDB&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install dbt-duckdb&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Databricks&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install dbt-databricks&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trino&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install dbt-trino&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each adapter has its own &lt;code&gt;profiles.yml&lt;/code&gt; format. The &lt;code&gt;type:&lt;/code&gt; field changes per adapter. For example:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PostgreSQL:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;my_project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev&lt;/span&gt;
  &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;dev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
      &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5432&lt;/span&gt;
      &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_database&lt;/span&gt;
      &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;public&lt;/span&gt;
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_user&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_password&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;BigQuery:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;my_project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev&lt;/span&gt;
  &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;dev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bigquery&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service-account&lt;/span&gt;
      &lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_gcp_project&lt;/span&gt;
      &lt;span class="na"&gt;dataset&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_dataset&lt;/span&gt;
      &lt;span class="na"&gt;keyfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/path/to/service-account.json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Snowflake:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;my_project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev&lt;/span&gt;
  &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;dev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;snowflake&lt;/span&gt;
      &lt;span class="na"&gt;account&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_account&lt;/span&gt;
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_user&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_password&lt;/span&gt;
      &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_database&lt;/span&gt;
      &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_schema&lt;/span&gt;
      &lt;span class="na"&gt;warehouse&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_warehouse&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_role&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Quick Reference: Most Used dbt Commands
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dbt debug              &lt;span class="c"&gt;# Test database connection&lt;/span&gt;
dbt deps               &lt;span class="c"&gt;# Install project package dependencies&lt;/span&gt;
dbt run                &lt;span class="c"&gt;# Run all models&lt;/span&gt;
dbt run &lt;span class="nt"&gt;--select&lt;/span&gt; model_name         &lt;span class="c"&gt;# Run a specific model&lt;/span&gt;
dbt run &lt;span class="nt"&gt;--target&lt;/span&gt; prod               &lt;span class="c"&gt;# Run against a specific target&lt;/span&gt;
dbt &lt;span class="nb"&gt;test&lt;/span&gt;                            &lt;span class="c"&gt;# Run data tests&lt;/span&gt;
dbt &lt;span class="nb"&gt;ls&lt;/span&gt;                              &lt;span class="c"&gt;# List all models&lt;/span&gt;
dbt compile                         &lt;span class="c"&gt;# Compile SQL without running&lt;/span&gt;
dbt docs generate &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; dbt docs serve &lt;span class="c"&gt;# Generate and view documentation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Summary of Installation Commands
&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;# Activate virtual environment&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; ~/your-project/env/bin/activate

&lt;span class="c"&gt;# Install dbt with SQL Server adapter&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;dbt-sqlserver

&lt;span class="c"&gt;# Install ODBC Driver (run one at a time)&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://packages.microsoft.com/keys/microsoft.asc | &lt;span class="nb"&gt;sudo &lt;/span&gt;gpg &lt;span class="nt"&gt;--dearmor&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /usr/share/keyrings/microsoft-prod.gpg
curl https://packages.microsoft.com/config/ubuntu/24.04/prod.list | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/mssql-release.list
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;&lt;span class="nv"&gt;ACCEPT_EULA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Y apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; msodbcsql18 unixodbc-dev

&lt;span class="c"&gt;# Verify ODBC driver&lt;/span&gt;
odbcinst &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"ODBC Driver 18 for SQL Server"&lt;/span&gt;

&lt;span class="c"&gt;# Clone project repo&lt;/span&gt;
git clone &amp;lt;REPO_URL&amp;gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; &amp;lt;project-folder&amp;gt;

&lt;span class="c"&gt;# Install dbt packages&lt;/span&gt;
dbt deps

&lt;span class="c"&gt;# Configure connection&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.dbt &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; nano ~/.dbt/profiles.yml

&lt;span class="c"&gt;# Test connection&lt;/span&gt;
dbt debug &lt;span class="nt"&gt;--target&lt;/span&gt; dev

&lt;span class="c"&gt;# Run models&lt;/span&gt;
dbt run &lt;span class="nt"&gt;--target&lt;/span&gt; dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;Written based on a real dbt + SQL Server setup on WSL2 Ubuntu 24.04 (Noble) with ODBC Driver 18.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>data</category>
      <category>science</category>
    </item>
    <item>
      <title>How to Install Apache Airflow 2.9.1 on WSL (Windows Subsystem for Linux)</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Tue, 09 Jun 2026 13:48:58 +0000</pubDate>
      <link>https://dev.to/msnmongare/how-to-install-apache-airflow-291-on-wsl-windows-subsystem-for-linux-2e1j</link>
      <guid>https://dev.to/msnmongare/how-to-install-apache-airflow-291-on-wsl-windows-subsystem-for-linux-2e1j</guid>
      <description>&lt;p&gt;Note: &lt;em&gt;A beginner-friendly, step-by-step guide for developers on Windows&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Apache Airflow?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://airflow.apache.org/" rel="noopener noreferrer"&gt;Apache Airflow&lt;/a&gt; is an open-source platform used to programmatically author, schedule, and monitor workflows. It's widely used in data engineering, MLOps, and backend automation pipelines. If you're building ETL pipelines, running scheduled tasks, or orchestrating complex workflows, Airflow is a tool you'll likely encounter.&lt;/p&gt;




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

&lt;p&gt;Before we begin, make sure you have the following in place:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Windows Subsystem for Linux (WSL) installed
&lt;/h3&gt;

&lt;p&gt;You need WSL 2 with &lt;strong&gt;Ubuntu 24.04 (Noble)&lt;/strong&gt; installed on your Windows machine. If you haven't set it up yet, open PowerShell as Administrator and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;wsl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--install&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart your machine when prompted, then launch &lt;strong&gt;Ubuntu&lt;/strong&gt; from the Start menu.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;a href="https://mobaxterm.mobatek.net/" rel="noopener noreferrer"&gt;MobiXterm&lt;/a&gt; or Windows Terminal (optional but recommended)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://mobaxterm.mobatek.net/" rel="noopener noreferrer"&gt;MobiXterm &lt;/a&gt;or Windows Terminal gives you a better terminal experience for working inside WSL. Either works fine.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Basic familiarity with the Linux terminal
&lt;/h3&gt;

&lt;p&gt;You don't need to be a Linux expert, but knowing how to navigate directories (&lt;code&gt;cd&lt;/code&gt;, &lt;code&gt;ls&lt;/code&gt;) and run commands will help.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Update Your System
&lt;/h2&gt;

&lt;p&gt;Open your WSL terminal and run:&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="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures your package lists and installed packages are up to date.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Install Python 3 and Check the Version
&lt;/h2&gt;

&lt;p&gt;Ubuntu 24.04 ships with Python 3.12 by default. Verify it's available:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You should see something like &lt;code&gt;Python 3.12.x&lt;/code&gt;. If not, install it:&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="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;python3 python3-dev &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3: Install &lt;code&gt;python3.12-venv&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Airflow should always be installed inside a virtual environment to avoid dependency conflicts with your system Python.&lt;/p&gt;

&lt;p&gt;First, install the venv module:&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="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;python3.12-venv &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Troubleshooting - Network/download errors?&lt;/strong&gt;&lt;br&gt;
On some networks, &lt;code&gt;apt&lt;/code&gt; may time out trying to download packages. If you see a &lt;code&gt;Connection timed out&lt;/code&gt; or &lt;code&gt;Connection failed&lt;/code&gt; error for &lt;code&gt;python3-pip-whl&lt;/code&gt;, use &lt;code&gt;wget&lt;/code&gt; to download it manually:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;wget &lt;span class="nt"&gt;-O&lt;/span&gt; python3-pip-whl.deb &lt;span class="s2"&gt;"https://launchpadlibrarian.net/753358547/python3-pip-whl_24.0+dfsg-1ubuntu1.3_all.deb"&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dpkg &lt;span class="nt"&gt;-i&lt;/span&gt; python3-pip-whl.deb
&lt;span class="nb"&gt;sudo &lt;/span&gt;dpkg &lt;span class="nt"&gt;--configure&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nt"&gt;--fix-broken&lt;/span&gt; &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Then retry the venv install:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;python3.12-venv &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 4: Create a Project Directory and Virtual Environment
&lt;/h2&gt;

&lt;p&gt;Create a dedicated folder for your Airflow installation:&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="nb"&gt;mkdir&lt;/span&gt; ~/airflow-2.9.1
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/airflow-2.9.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now create a virtual environment inside it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Activate the virtual 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="nb"&gt;source &lt;/span&gt;venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your terminal prompt should now show &lt;code&gt;(venv)&lt;/code&gt; at the beginning, like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;(venv) user@DESKTOP-XXXX:~/airflow-2.9.1$&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This confirms you are inside the virtual environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Upgrade pip
&lt;/h2&gt;

&lt;p&gt;Before installing Airflow, upgrade pip to the latest version:&lt;br&gt;
&lt;/p&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; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; pip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 6: Install Apache Airflow 2.9.1
&lt;/h2&gt;

&lt;p&gt;Install Airflow using the official constraints file. This is &lt;strong&gt;important&lt;/strong&gt; - Airflow has many dependencies, and the constraints file ensures compatible versions are installed together without conflicts.&lt;br&gt;
&lt;/p&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;apache-airflow&lt;span class="o"&gt;==&lt;/span&gt;2.9.1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--constraint&lt;/span&gt; &lt;span class="s2"&gt;"https://raw.githubusercontent.com/apache/airflow/constraints-2.9.1/constraints-3.12.txt"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This may take a few minutes. Once complete, verify the installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;airflow version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 7: Initialize the Airflow Database
&lt;/h2&gt;

&lt;p&gt;Airflow uses a metadata database to track DAG runs, tasks, logs, and more. Initialize it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;airflow db init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates the default SQLite database and the &lt;code&gt;~/airflow&lt;/code&gt; directory with all necessary config files.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 8: Create an Admin User
&lt;/h2&gt;

&lt;p&gt;Create your first admin user to log into the Airflow web UI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;airflow &lt;span class="nb"&gt;users &lt;/span&gt;create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--username&lt;/span&gt; admin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--firstname&lt;/span&gt; YourFirstName &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--lastname&lt;/span&gt; YourLastName &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role&lt;/span&gt; Admin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--email&lt;/span&gt; your@email.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will be prompted to set a password. Enter and confirm it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 9: Start the Airflow Web Server and Scheduler
&lt;/h2&gt;

&lt;p&gt;Airflow requires two processes running simultaneously - the &lt;strong&gt;webserver&lt;/strong&gt; and the &lt;strong&gt;scheduler&lt;/strong&gt;. Open two separate WSL terminal tabs or windows for this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Terminal 1 - Start the webserver:&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;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/airflow-2.9.1
&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate
airflow webserver &lt;span class="nt"&gt;--port&lt;/span&gt; 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Terminal 2 - Start the scheduler:&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;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/airflow-2.9.1
&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate
airflow scheduler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 10: Access the Airflow UI
&lt;/h2&gt;

&lt;p&gt;Open your browser and navigate to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see the Airflow login page. Enter the username and password you created in Step 8.&lt;/p&gt;

&lt;p&gt;You should now be inside the &lt;strong&gt;Airflow Dashboard&lt;/strong&gt; - ready to create and manage your DAGs!&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Reference: Starting Airflow After a Reboot
&lt;/h2&gt;

&lt;p&gt;Every time you restart your machine and want to use Airflow, run these commands in two terminals:&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;# Both terminals: navigate and activate venv&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/airflow-2.9.1
&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate

&lt;span class="c"&gt;# Terminal 1&lt;/span&gt;
airflow webserver &lt;span class="nt"&gt;--port&lt;/span&gt; 8080

&lt;span class="c"&gt;# Terminal 2&lt;/span&gt;
airflow scheduler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Summary of All Commands
&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;# Update system&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# Install Python venv&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;python3.12-venv &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# Create project directory and venv&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; ~/airflow-2.9.1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ~/airflow-2.9.1
python3 &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 Airflow&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; pip
pip &lt;span class="nb"&gt;install &lt;/span&gt;apache-airflow&lt;span class="o"&gt;==&lt;/span&gt;2.9.1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--constraint&lt;/span&gt; &lt;span class="s2"&gt;"https://raw.githubusercontent.com/apache/airflow/constraints-2.9.1/constraints-3.12.txt"&lt;/span&gt;

&lt;span class="c"&gt;# Verify&lt;/span&gt;
airflow version

&lt;span class="c"&gt;# Initialize DB and create user&lt;/span&gt;
airflow db init
airflow &lt;span class="nb"&gt;users &lt;/span&gt;create &lt;span class="nt"&gt;--username&lt;/span&gt; admin &lt;span class="nt"&gt;--firstname&lt;/span&gt; YourFirstName &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--lastname&lt;/span&gt; YourLastName &lt;span class="nt"&gt;--role&lt;/span&gt; Admin &lt;span class="nt"&gt;--email&lt;/span&gt; your@email.com

&lt;span class="c"&gt;# Start services&lt;/span&gt;
airflow webserver &lt;span class="nt"&gt;--port&lt;/span&gt; 8080   &lt;span class="c"&gt;# Terminal 1&lt;/span&gt;
airflow scheduler               &lt;span class="c"&gt;# Terminal 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;p&gt;Now that Airflow is running, here are some things to explore next:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Write your first DAG&lt;/strong&gt; - create a Python file in &lt;code&gt;~/airflow/dags/&lt;/code&gt; to define a workflow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explore Airflow Operators&lt;/strong&gt; - BashOperator, PythonOperator, and more&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set up PostgreSQL&lt;/strong&gt; as the Airflow backend (recommended for production over SQLite)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use the CeleryExecutor&lt;/strong&gt; for parallel task execution&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>linux</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Install `python-dotenv` in Python</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Tue, 02 Jun 2026 04:31:13 +0000</pubDate>
      <link>https://dev.to/msnmongare/how-to-install-python-dotenv-in-python-2m9f</link>
      <guid>https://dev.to/msnmongare/how-to-install-python-dotenv-in-python-2m9f</guid>
      <description>&lt;p&gt;Environment variables are an essential part of modern software development. They allow developers to store sensitive information such as API keys, database credentials, and secret tokens outside the source code. In Python, one of the most popular libraries for managing environment variables is &lt;strong&gt;python-dotenv&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This article will guide you through installing and using &lt;code&gt;python-dotenv&lt;/code&gt; in your Python projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is &lt;code&gt;python-dotenv&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;python-dotenv&lt;/code&gt; is a Python package that reads key-value pairs from a &lt;code&gt;.env&lt;/code&gt; file and loads them into environment variables. This helps keep sensitive configuration data separate from your codebase.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits of Using &lt;code&gt;python-dotenv&lt;/code&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Keeps secrets out of source code&lt;/li&gt;
&lt;li&gt;Simplifies configuration management&lt;/li&gt;
&lt;li&gt;Makes applications easier to deploy across different environments&lt;/li&gt;
&lt;li&gt;Works seamlessly with frameworks like Django, Flask, and FastAPI&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1: Install &lt;code&gt;python-dotenv&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Open your terminal or command prompt and run:&lt;br&gt;
&lt;/p&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;python-dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're using Python 3 and your system requires &lt;code&gt;pip3&lt;/code&gt;, use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip3 &lt;span class="nb"&gt;install &lt;/span&gt;python-dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To verify the installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip show python-dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see package information similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Name: python-dotenv
Version: 1.x.x
Summary: Read key-value pairs from a .env file and set them as environment variables
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 2: Create a &lt;code&gt;.env&lt;/code&gt; File
&lt;/h2&gt;

&lt;p&gt;In the root directory of your project, create a file named:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Add your environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API_KEY=your_api_key_here
DATABASE_URL=mysql://user:password@localhost/dbname
DEBUG=True
SECRET_KEY=my_secret_key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3: Load Environment Variables
&lt;/h2&gt;

&lt;p&gt;Create a Python file and load the variables using &lt;code&gt;load_dotenv()&lt;/code&gt;.&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;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="c1"&gt;# Load variables from .env file
&lt;/span&gt;&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;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="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;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;debug&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="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEBUG&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;api_key&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;debug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you run the script, the values from the &lt;code&gt;.env&lt;/code&gt; file will be loaded into your application.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Using &lt;code&gt;python-dotenv&lt;/code&gt; in Django
&lt;/h2&gt;

&lt;p&gt;Install the package:&lt;br&gt;
&lt;/p&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;python-dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your &lt;code&gt;settings.py&lt;/code&gt; file:&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;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;SECRET_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="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SECRET_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;DEBUG&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="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEBUG&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;True&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your &lt;code&gt;.env&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SECRET_KEY=django-secret-key
DEBUG=True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prevents sensitive settings from being hardcoded into your project.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Using &lt;code&gt;python-dotenv&lt;/code&gt; in Flask
&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;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;

&lt;span class="nf"&gt;load_dotenv&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="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SECRET_KEY&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="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&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;def&lt;/span&gt; &lt;span class="nf"&gt;home&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;Hello World&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;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 6: Keep Your &lt;code&gt;.env&lt;/code&gt; File Out of Git
&lt;/h2&gt;

&lt;p&gt;Never commit your &lt;code&gt;.env&lt;/code&gt; file to version control.&lt;/p&gt;

&lt;p&gt;Add it to &lt;code&gt;.gitignore&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;.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures sensitive information remains private.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Environment Variables
&lt;/h2&gt;

&lt;p&gt;Example &lt;code&gt;.env&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASE_HOST=localhost
DATABASE_PORT=3306
DATABASE_NAME=mydb
DATABASE_USER=root
DATABASE_PASSWORD=password

OPENAI_API_KEY=your_openai_key
WHATSAPP_TOKEN=your_whatsapp_token

DEBUG=True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Access them in Python:&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="n"&gt;host&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="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DATABASE_HOST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;port&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="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DATABASE_PORT&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;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. ModuleNotFoundError
&lt;/h3&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ModuleNotFoundError: No module named 'dotenv'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Solution:&lt;br&gt;
&lt;/p&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;python-dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure you're installing it in the same virtual environment you're running your application from.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Variables Returning &lt;code&gt;None&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Check that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;.env&lt;/code&gt; file exists in the project root.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;load_dotenv()&lt;/code&gt; is called before &lt;code&gt;os.getenv()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Variable names match exactly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&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="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;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="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;API_KEY&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;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Never commit &lt;code&gt;.env&lt;/code&gt; files to Git repositories.&lt;/li&gt;
&lt;li&gt;Use meaningful variable names.&lt;/li&gt;
&lt;li&gt;Store all secrets in environment variables.&lt;/li&gt;
&lt;li&gt;Use separate &lt;code&gt;.env&lt;/code&gt; files for development, staging, and production.&lt;/li&gt;
&lt;li&gt;Validate required environment variables when your application starts.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example:&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;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;

&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;required_vars&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;API_KEY&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;DATABASE_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;required_vars&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&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;Missing environment variable: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;var&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;python-dotenv&lt;/code&gt; package provides a simple and secure way to manage configuration settings in Python applications. By storing sensitive information in a &lt;code&gt;.env&lt;/code&gt; file and loading it with &lt;code&gt;load_dotenv()&lt;/code&gt;, you can keep your code clean, secure, and easy to maintain. Whether you're building a Django application, Flask API, FastAPI service, or automation script, &lt;code&gt;python-dotenv&lt;/code&gt; is a valuable tool that every Python developer should know.&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>AI Workflows vs AI Agents: Understanding the Difference and When to Use Each</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Fri, 29 May 2026 09:27:54 +0000</pubDate>
      <link>https://dev.to/msnmongare/ai-workflows-vs-ai-agents-understanding-the-difference-and-when-to-use-each-47ne</link>
      <guid>https://dev.to/msnmongare/ai-workflows-vs-ai-agents-understanding-the-difference-and-when-to-use-each-47ne</guid>
      <description>&lt;p&gt;Artificial intelligence systems today are often described as either workflows or agents. While they may seem similar because both use AI models, they are fundamentally different in how they operate, how much autonomy they have, and what problems they are best suited for.&lt;/p&gt;

&lt;p&gt;Understanding this distinction is important for anyone building automation systems, backend services, or AI-powered products.&lt;/p&gt;




&lt;h1&gt;
  
  
  What is an AI Workflow?
&lt;/h1&gt;

&lt;p&gt;An AI workflow is a structured, predefined sequence of steps where AI is used at specific points in the process.&lt;/p&gt;

&lt;p&gt;In a workflow, a human designs the entire process in advance. The AI does not decide what to do next. It simply performs tasks within the steps that have already been defined.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key idea
&lt;/h2&gt;

&lt;p&gt;The process is fixed. Only the execution uses AI.&lt;/p&gt;




&lt;h2&gt;
  
  
  Example of an AI workflow
&lt;/h2&gt;

&lt;p&gt;A simple customer support automation might look like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A user submits a support ticket&lt;/li&gt;
&lt;li&gt;AI summarizes the message&lt;/li&gt;
&lt;li&gt;AI classifies the urgency level&lt;/li&gt;
&lt;li&gt;The ticket is routed to the correct department&lt;/li&gt;
&lt;li&gt;The customer receives an automated response&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each step is predefined. The system always follows the same path.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tools commonly used for AI workflows
&lt;/h2&gt;

&lt;p&gt;AI workflows are usually built using automation platforms such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://n8n.io?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;n8n&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.make.com?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Make&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zapier.com?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Zapier&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://powerautomate.microsoft.com?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Microsoft Power Automate&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These tools allow you to visually design step-by-step automations and insert AI models where needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Characteristics of AI workflows
&lt;/h2&gt;

&lt;p&gt;AI workflows typically have the following traits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Step-by-step execution&lt;/li&gt;
&lt;li&gt;Predictable outcomes&lt;/li&gt;
&lt;li&gt;Mostly deterministic logic&lt;/li&gt;
&lt;li&gt;Easier debugging and maintenance&lt;/li&gt;
&lt;li&gt;Faster to build and deploy&lt;/li&gt;
&lt;li&gt;Best suited for repetitive business processes&lt;/li&gt;
&lt;li&gt;Limited decision-making ability&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Real-world use cases
&lt;/h2&gt;

&lt;p&gt;AI workflows are widely used in production systems such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Email summarization systems&lt;/li&gt;
&lt;li&gt;Auto-posting content to social media&lt;/li&gt;
&lt;li&gt;Invoice processing pipelines&lt;/li&gt;
&lt;li&gt;CRM data updates&lt;/li&gt;
&lt;li&gt;WhatsApp auto-replies&lt;/li&gt;
&lt;li&gt;Lead qualification systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They are especially strong in business environments where reliability and consistency matter more than flexibility.&lt;/p&gt;




&lt;h1&gt;
  
  
  What is an AI Agent?
&lt;/h1&gt;

&lt;p&gt;An AI agent is a more autonomous system that can make decisions about how to complete a task.&lt;/p&gt;

&lt;p&gt;Instead of following a fixed sequence of steps, an AI agent works toward a goal and determines the best actions to take dynamically.&lt;/p&gt;

&lt;p&gt;You provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A goal or objective&lt;/li&gt;
&lt;li&gt;Access to tools or APIs&lt;/li&gt;
&lt;li&gt;Optional memory or context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agent then figures out how to achieve the goal.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key idea
&lt;/h2&gt;

&lt;p&gt;The process is not fully predefined. The AI decides the steps.&lt;/p&gt;




&lt;h2&gt;
  
  
  Example of an AI agent
&lt;/h2&gt;

&lt;p&gt;If you give an agent a task like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Find 10 Kenyan fintech APIs, summarize them, and prepare documentation drafts.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent may:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Search the web for fintech APIs&lt;/li&gt;
&lt;li&gt;Open documentation pages&lt;/li&gt;
&lt;li&gt;Extract endpoints and features&lt;/li&gt;
&lt;li&gt;Summarize each API&lt;/li&gt;
&lt;li&gt;Organize results into structured notes&lt;/li&gt;
&lt;li&gt;Save or export the information&lt;/li&gt;
&lt;li&gt;Retry failed steps if needed&lt;/li&gt;
&lt;li&gt;Ask clarifying questions if something is unclear&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The important part is that the exact path is not prewritten.&lt;/p&gt;




&lt;h2&gt;
  
  
  Frameworks used for building AI agents
&lt;/h2&gt;

&lt;p&gt;Modern AI agents are built using specialized frameworks such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.langchain.com?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;LangChain&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openai.github.io/openai-agents-python/?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;OpenAI Agents SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://microsoft.github.io/autogen/?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Microsoft AutoGen&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.crewai.com?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;CrewAI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/agent-framework/?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Microsoft Agent Framework&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These frameworks allow models to reason, use tools, maintain memory, and coordinate multi-step tasks.&lt;/p&gt;




&lt;h2&gt;
  
  
  Characteristics of AI agents
&lt;/h2&gt;

&lt;p&gt;AI agents typically have these properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Goal-oriented behavior&lt;/li&gt;
&lt;li&gt;Higher autonomy&lt;/li&gt;
&lt;li&gt;Dynamic decision-making&lt;/li&gt;
&lt;li&gt;Ability to use multiple tools&lt;/li&gt;
&lt;li&gt;Reasoning over steps&lt;/li&gt;
&lt;li&gt;Memory or context awareness&lt;/li&gt;
&lt;li&gt;More complex to design and control&lt;/li&gt;
&lt;li&gt;Flexible problem-solving capability&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Real-world use cases
&lt;/h2&gt;

&lt;p&gt;AI agents are used for more advanced and adaptive tasks such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Research assistants that gather and summarize information&lt;/li&gt;
&lt;li&gt;Coding agents that write and debug software&lt;/li&gt;
&lt;li&gt;Sales assistants that qualify and follow up on leads&lt;/li&gt;
&lt;li&gt;Data analysis assistants that explore datasets dynamically&lt;/li&gt;
&lt;li&gt;Personal productivity assistants that manage tasks and schedules&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  AI Workflow vs AI Agent: The Core Difference
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;AI Workflow&lt;/th&gt;
&lt;th&gt;AI Agent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Process structure&lt;/td&gt;
&lt;td&gt;Fixed&lt;/td&gt;
&lt;td&gt;Flexible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Decision-making&lt;/td&gt;
&lt;td&gt;Human-defined&lt;/td&gt;
&lt;td&gt;AI-driven&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Predictability&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium to low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Autonomy&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complexity&lt;/td&gt;
&lt;td&gt;Easier&lt;/td&gt;
&lt;td&gt;More complex&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debugging&lt;/td&gt;
&lt;td&gt;Simple&lt;/td&gt;
&lt;td&gt;Harder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best use case&lt;/td&gt;
&lt;td&gt;Repetitive automation&lt;/td&gt;
&lt;td&gt;Complex problem solving&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h1&gt;
  
  
  Simple Analogy
&lt;/h1&gt;

&lt;p&gt;A simple way to understand the difference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI workflow is like a GPS with a fixed route already planned&lt;/li&gt;
&lt;li&gt;AI agent is like a driver who decides routes dynamically based on traffic, obstacles, and conditions&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Example Comparison
&lt;/h1&gt;

&lt;h2&gt;
  
  
  AI Workflow Example
&lt;/h2&gt;

&lt;p&gt;Using &lt;a href="https://n8n.io?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;n8n&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Gmail trigger receives a message&lt;/li&gt;
&lt;li&gt;AI summarizes the email&lt;/li&gt;
&lt;li&gt;The summary is saved to Notion&lt;/li&gt;
&lt;li&gt;A Slack notification is sent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything is predefined and consistent.&lt;/p&gt;




&lt;h2&gt;
  
  
  AI Agent Example
&lt;/h2&gt;

&lt;p&gt;An AI assistant system may:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read incoming emails&lt;/li&gt;
&lt;li&gt;Decide which ones are important&lt;/li&gt;
&lt;li&gt;Draft responses automatically&lt;/li&gt;
&lt;li&gt;Schedule meetings&lt;/li&gt;
&lt;li&gt;Search the web for missing context&lt;/li&gt;
&lt;li&gt;Follow up on unanswered messages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The system decides what to do at each step.&lt;/p&gt;




&lt;h1&gt;
  
  
  Important Reality in Today’s AI Landscape
&lt;/h1&gt;

&lt;p&gt;Although the term “AI agent” is widely used, many real-world systems marketed as agents are actually advanced AI workflows with some dynamic decision-making added.&lt;/p&gt;

&lt;p&gt;Fully autonomous agents are still challenging because they are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hard to control reliably&lt;/li&gt;
&lt;li&gt;Expensive to run at scale&lt;/li&gt;
&lt;li&gt;Difficult to debug when things go wrong&lt;/li&gt;
&lt;li&gt;Sometimes unpredictable in production systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because of this, many companies prefer a hybrid approach.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Best Practical Approach Today
&lt;/h1&gt;

&lt;p&gt;In real-world systems, the most effective architecture is often a combination of both:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI workflows provide structure and reliability&lt;/li&gt;
&lt;li&gt;AI agents provide intelligence and flexibility inside parts of the workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This hybrid model gives the best balance between control and adaptability.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;AI workflows are structured and predictable systems enhanced with AI&lt;/li&gt;
&lt;li&gt;AI agents are goal-driven systems that can decide their own steps&lt;/li&gt;
&lt;li&gt;Workflows are best for automation and business processes&lt;/li&gt;
&lt;li&gt;Agents are best for complex, adaptive problem-solving&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice, modern AI products rarely choose one or the other exclusively. They blend both to build systems that are reliable, intelligent, and scalable.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>openai</category>
      <category>automation</category>
    </item>
    <item>
      <title>Understanding MCP (Model Context Protocol): The Future of AI Integrations</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Wed, 20 May 2026 11:53:28 +0000</pubDate>
      <link>https://dev.to/msnmongare/understanding-mcp-model-context-protocol-the-future-of-ai-integrations-2kdm</link>
      <guid>https://dev.to/msnmongare/understanding-mcp-model-context-protocol-the-future-of-ai-integrations-2kdm</guid>
      <description>&lt;p&gt;Artificial Intelligence is rapidly moving beyond simple chatbots. Today, AI systems are becoming assistants that can search databases, read files, interact with APIs, automate workflows, and even operate business systems.&lt;/p&gt;

&lt;p&gt;One of the technologies making this possible is MCP, short for Model Context Protocol.&lt;/p&gt;

&lt;p&gt;If you are hearing about MCP for the first time, this article will help you understand what it is, why it matters, how it works, and why developers are paying close attention to it.&lt;/p&gt;




&lt;h1&gt;
  
  
  What is MCP?
&lt;/h1&gt;

&lt;p&gt;MCP (Model Context Protocol) is a standardized way for AI models to connect with tools, applications, databases, APIs, and external systems.&lt;/p&gt;

&lt;p&gt;In simple terms:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;MCP acts like a bridge between AI and software systems.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Without MCP, AI assistants mostly rely on text conversations. They can answer questions, but they cannot naturally interact with real-world systems unless developers build custom integrations for every single tool.&lt;/p&gt;

&lt;p&gt;MCP changes this by introducing a common communication standard.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why MCP Matters
&lt;/h1&gt;

&lt;p&gt;Modern AI systems are becoming more capable every day, but intelligence alone is not enough.&lt;/p&gt;

&lt;p&gt;For AI to become truly useful in businesses and applications, it needs access to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Databases&lt;/li&gt;
&lt;li&gt;APIs&lt;/li&gt;
&lt;li&gt;File systems&lt;/li&gt;
&lt;li&gt;Cloud platforms&lt;/li&gt;
&lt;li&gt;CRMs&lt;/li&gt;
&lt;li&gt;Developer tools&lt;/li&gt;
&lt;li&gt;Internal company systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The challenge is that every system works differently.&lt;/p&gt;

&lt;p&gt;One application may use REST APIs.&lt;br&gt;
Another may use GraphQL.&lt;br&gt;
Another may require SQL queries.&lt;br&gt;
Another may use completely custom workflows.&lt;/p&gt;

&lt;p&gt;Without standardization, developers must create separate integrations for every tool.&lt;/p&gt;

&lt;p&gt;MCP solves this problem.&lt;/p&gt;


&lt;h1&gt;
  
  
  A Simple Analogy
&lt;/h1&gt;

&lt;p&gt;Think of MCP as a universal adapter for AI.&lt;/p&gt;

&lt;p&gt;Different software systems are like different power sockets around the world.&lt;/p&gt;

&lt;p&gt;Without an adapter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;nothing connects properly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MCP provides a standardized way for AI systems to communicate with all these tools using one common structure.&lt;/p&gt;

&lt;p&gt;Another good analogy is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;APIs are individual roads&lt;/li&gt;
&lt;li&gt;MCP is the traffic system organizing everything&lt;/li&gt;
&lt;/ul&gt;


&lt;h1&gt;
  
  
  How MCP Works
&lt;/h1&gt;

&lt;p&gt;At a high level, MCP involves four main components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The User&lt;/li&gt;
&lt;li&gt;The AI Assistant&lt;/li&gt;
&lt;li&gt;The MCP Client/Server&lt;/li&gt;
&lt;li&gt;External Tools or Systems&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The 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;User
  ↓
AI Assistant
  ↓
MCP Client
  ↓
MCP Server
  ↓
Tools / APIs / Databases
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Step-by-Step Example
&lt;/h1&gt;

&lt;p&gt;Imagine a user asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Show me all failed payments from today and summarize the issue.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here is what happens behind the scenes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: AI Understands the Request
&lt;/h2&gt;

&lt;p&gt;The AI realizes it needs payment transaction data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: MCP Discovers Available Tools
&lt;/h2&gt;

&lt;p&gt;The MCP server may expose tools such as:&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="s2"&gt;"search_transactions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"get_failed_payments"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"generate_report"&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 AI can automatically discover what tools are available.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: AI Calls a Tool
&lt;/h2&gt;

&lt;p&gt;The AI sends a structured request such as:&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;"tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"get_failed_payments"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"date"&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-05-20"&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;h2&gt;
  
  
  Step 4: MCP Server Executes the Request
&lt;/h2&gt;

&lt;p&gt;The MCP server:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;connects to databases&lt;/li&gt;
&lt;li&gt;fetches data&lt;/li&gt;
&lt;li&gt;processes results&lt;/li&gt;
&lt;li&gt;returns structured information&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 5: AI Responds Naturally
&lt;/h2&gt;

&lt;p&gt;The AI finally responds:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“There were 37 failed transactions today. Most failures were caused by insufficient balance.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The user gets a natural conversation experience while MCP handles the technical communication in the background.&lt;/p&gt;




&lt;h1&gt;
  
  
  MCP vs APIs
&lt;/h1&gt;

&lt;p&gt;Many beginners confuse MCP with APIs, but they are different.&lt;/p&gt;

&lt;h2&gt;
  
  
  APIs
&lt;/h2&gt;

&lt;p&gt;APIs are direct communication channels between software systems.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application A → API → Application B
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each API has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;different endpoints&lt;/li&gt;
&lt;li&gt;different authentication&lt;/li&gt;
&lt;li&gt;different request formats&lt;/li&gt;
&lt;li&gt;different documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Developers must learn every API separately.&lt;/p&gt;




&lt;h2&gt;
  
  
  MCP
&lt;/h2&gt;

&lt;p&gt;MCP standardizes how AI interacts with these systems.&lt;/p&gt;

&lt;p&gt;Instead of teaching AI how every system works individually, MCP provides one common structure.&lt;/p&gt;

&lt;p&gt;You can think of it this way:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;APIs&lt;/th&gt;
&lt;th&gt;MCP&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Software-to-software communication&lt;/td&gt;
&lt;td&gt;AI-to-software communication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Every integration is custom&lt;/td&gt;
&lt;td&gt;Standardized integration approach&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Focused on applications&lt;/td&gt;
&lt;td&gt;Focused on AI agents and assistants&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Different structures everywhere&lt;/td&gt;
&lt;td&gt;Common protocol structure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h1&gt;
  
  
  Why Developers Are Excited About MCP
&lt;/h1&gt;

&lt;p&gt;MCP opens the door to a new generation of AI-powered systems.&lt;/p&gt;

&lt;p&gt;Instead of building simple chatbots, developers can build:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI agents&lt;/li&gt;
&lt;li&gt;AI copilots&lt;/li&gt;
&lt;li&gt;Intelligent automation systems&lt;/li&gt;
&lt;li&gt;Operational assistants&lt;/li&gt;
&lt;li&gt;AI support systems&lt;/li&gt;
&lt;li&gt;AI developer tools&lt;/li&gt;
&lt;li&gt;Enterprise AI platforms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is one reason why AI engineering is evolving so quickly.&lt;/p&gt;




&lt;h1&gt;
  
  
  Real-World Use Cases of MCP
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1. Customer Support AI
&lt;/h2&gt;

&lt;p&gt;An AI assistant can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;check customer accounts&lt;/li&gt;
&lt;li&gt;search transactions&lt;/li&gt;
&lt;li&gt;issue refunds&lt;/li&gt;
&lt;li&gt;generate reports&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;all through MCP-connected systems.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Developer Assistants
&lt;/h2&gt;

&lt;p&gt;AI coding assistants can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;read repositories&lt;/li&gt;
&lt;li&gt;create pull requests&lt;/li&gt;
&lt;li&gt;analyze logs&lt;/li&gt;
&lt;li&gt;interact with CI/CD pipelines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;through MCP integrations.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Business Automation
&lt;/h2&gt;

&lt;p&gt;Companies can build AI systems that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;automate workflows&lt;/li&gt;
&lt;li&gt;generate analytics&lt;/li&gt;
&lt;li&gt;manage operations&lt;/li&gt;
&lt;li&gt;monitor systems&lt;/li&gt;
&lt;li&gt;coordinate teams&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;using MCP servers connected to internal tools.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Banking and Fintech
&lt;/h2&gt;

&lt;p&gt;A banking MCP server could expose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;account lookup&lt;/li&gt;
&lt;li&gt;transaction history&lt;/li&gt;
&lt;li&gt;payment reversal&lt;/li&gt;
&lt;li&gt;fraud detection tools&lt;/li&gt;
&lt;li&gt;reporting systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;allowing AI systems to assist operations securely.&lt;/p&gt;




&lt;h1&gt;
  
  
  MCP and AI Agents
&lt;/h1&gt;

&lt;p&gt;MCP is becoming extremely important in the world of AI agents.&lt;/p&gt;

&lt;p&gt;AI agents are systems that can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reason&lt;/li&gt;
&lt;li&gt;make decisions&lt;/li&gt;
&lt;li&gt;use tools&lt;/li&gt;
&lt;li&gt;perform tasks&lt;/li&gt;
&lt;li&gt;execute workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For agents to work effectively, they need reliable access to tools and data.&lt;/p&gt;

&lt;p&gt;MCP provides that infrastructure.&lt;/p&gt;

&lt;p&gt;This is why MCP is frequently mentioned alongside topics like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RAG (Retrieval-Augmented Generation)&lt;/li&gt;
&lt;li&gt;AI workflows&lt;/li&gt;
&lt;li&gt;orchestration&lt;/li&gt;
&lt;li&gt;agent memory&lt;/li&gt;
&lt;li&gt;multi-agent systems&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  MCP for Backend Developers
&lt;/h1&gt;

&lt;p&gt;If you are a backend developer working with frameworks like Django, Node.js, Laravel, or Spring Boot, MCP creates exciting opportunities.&lt;/p&gt;

&lt;p&gt;For example, your backend can expose MCP-compatible tools such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;transaction search&lt;/li&gt;
&lt;li&gt;analytics generation&lt;/li&gt;
&lt;li&gt;user management&lt;/li&gt;
&lt;li&gt;reporting systems&lt;/li&gt;
&lt;li&gt;workflow automation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This allows AI systems to interact with your platform intelligently.&lt;/p&gt;

&lt;p&gt;Traditional architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Frontend → Backend API → Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI-enabled architecture with MCP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI Assistant → MCP Server → Backend → Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is one reason many developers believe MCP will become a major part of modern software architecture.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Future of MCP
&lt;/h1&gt;

&lt;p&gt;As AI systems continue to evolve, standardization becomes increasingly important.&lt;/p&gt;

&lt;p&gt;MCP could become a foundational layer for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;enterprise AI systems&lt;/li&gt;
&lt;li&gt;AI operating systems&lt;/li&gt;
&lt;li&gt;AI workplace assistants&lt;/li&gt;
&lt;li&gt;autonomous software agents&lt;/li&gt;
&lt;li&gt;intelligent business platforms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just as APIs transformed web development, MCP may transform AI integration.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;MCP is not replacing APIs.&lt;/p&gt;

&lt;p&gt;Instead, it builds on top of existing systems and makes them easier for AI to understand and use.&lt;/p&gt;

&lt;p&gt;The key idea is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;APIs help software communicate with software.&lt;/p&gt;

&lt;p&gt;MCP helps AI communicate with software intelligently and consistently.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As AI continues moving from conversation to action, MCP is becoming one of the most important concepts for developers, businesses, and AI engineers to understand.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>The Stages of AI in Software Engineering - And Where We Are Today</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Wed, 15 Apr 2026 11:36:28 +0000</pubDate>
      <link>https://dev.to/msnmongare/the-stages-of-ai-in-software-engineering-and-where-we-are-today-1odf</link>
      <guid>https://dev.to/msnmongare/the-stages-of-ai-in-software-engineering-and-where-we-are-today-1odf</guid>
      <description>&lt;p&gt;Software engineering hasn’t just evolved. It has accelerated.&lt;/p&gt;

&lt;p&gt;In the last couple of years, the way we build software has changed more than most people realize. Many developers are still operating in older patterns, while the ground beneath them has already shifted.&lt;/p&gt;

&lt;p&gt;To understand where we are going, it helps to break things down into stages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 1: Ask AI
&lt;/h2&gt;

&lt;p&gt;This is where it started for most people.&lt;/p&gt;

&lt;p&gt;AI was used like a smarter search engine. You would:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;paste an error&lt;/li&gt;
&lt;li&gt;ask for a function&lt;/li&gt;
&lt;li&gt;get a quick fix&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing about your workflow really changed.&lt;/p&gt;

&lt;p&gt;You were still:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;thinking through the problem&lt;/li&gt;
&lt;li&gt;designing the solution&lt;/li&gt;
&lt;li&gt;writing most of the code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI was just helping with syntax and speed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stage 2: AI in Your Editor
&lt;/h2&gt;

&lt;p&gt;Then AI moved closer.&lt;/p&gt;

&lt;p&gt;Instead of switching tabs, it started living inside your editor. It could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;autocomplete your thoughts&lt;/li&gt;
&lt;li&gt;suggest full functions&lt;/li&gt;
&lt;li&gt;understand your code context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This felt powerful. And it was.&lt;/p&gt;

&lt;p&gt;But the control didn’t change.&lt;/p&gt;

&lt;p&gt;You were still driving.&lt;br&gt;
AI was just helping you move faster.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stage 3: Describe the Problem
&lt;/h2&gt;

&lt;p&gt;This is where the real shift began.&lt;/p&gt;

&lt;p&gt;Instead of asking:&lt;br&gt;
“How do I build this?”&lt;/p&gt;

&lt;p&gt;You started asking:&lt;br&gt;
“Here’s what I want. Build it.”&lt;/p&gt;

&lt;p&gt;The focus moved away from code and into clarity.&lt;/p&gt;

&lt;p&gt;You describe:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the feature&lt;/li&gt;
&lt;li&gt;the behavior&lt;/li&gt;
&lt;li&gt;the expected outcome&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI handles the implementation.&lt;/p&gt;

&lt;p&gt;At this stage, your value is no longer how fast you can type.&lt;br&gt;
It’s how clearly you can think.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stage 4: Manage AI Agents (Where We Are Now)
&lt;/h2&gt;

&lt;p&gt;We are no longer just working with a single AI.&lt;/p&gt;

&lt;p&gt;We are starting to work with multiple agents at once.&lt;/p&gt;

&lt;p&gt;Instead of writing code line by line, you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;assign tasks&lt;/li&gt;
&lt;li&gt;run workflows in parallel&lt;/li&gt;
&lt;li&gt;review outputs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One agent can build a feature.&lt;br&gt;
Another writes tests.&lt;br&gt;
Another refactors existing code.&lt;/p&gt;

&lt;p&gt;This no longer feels like coding.&lt;/p&gt;

&lt;p&gt;It feels like managing a team.&lt;/p&gt;

&lt;p&gt;And that changes everything.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stage 5: Autonomous AI Systems (What’s Next)
&lt;/h2&gt;

&lt;p&gt;The next step is already taking shape.&lt;/p&gt;

&lt;p&gt;Agents won’t just work in parallel.&lt;br&gt;
They will work together.&lt;/p&gt;

&lt;p&gt;A single request could trigger a full pipeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;build&lt;/li&gt;
&lt;li&gt;test&lt;/li&gt;
&lt;li&gt;validate&lt;/li&gt;
&lt;li&gt;deploy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With little to no human interruption.&lt;/p&gt;

&lt;p&gt;Your role becomes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;defining requirements&lt;/li&gt;
&lt;li&gt;setting boundaries&lt;/li&gt;
&lt;li&gt;reviewing final outcomes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You are no longer in the loop at every step.&lt;br&gt;
You step in when it matters.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stage 6: Self-Evolving Systems (The Future)
&lt;/h2&gt;

&lt;p&gt;This is where things get even more interesting.&lt;/p&gt;

&lt;p&gt;Software will not just run.&lt;br&gt;
It will improve itself.&lt;/p&gt;

&lt;p&gt;Systems will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;learn from user behavior&lt;/li&gt;
&lt;li&gt;adjust features automatically&lt;/li&gt;
&lt;li&gt;optimize performance continuously&lt;/li&gt;
&lt;li&gt;refine their own logic over time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Software becomes something closer to a living system.&lt;/p&gt;

&lt;p&gt;Not static. Not finished. Always adapting.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Means for Engineers
&lt;/h2&gt;

&lt;p&gt;The role of a software engineer is not disappearing.&lt;br&gt;
But it is changing.&lt;/p&gt;

&lt;p&gt;Less focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;writing every line of code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;defining problems clearly&lt;/li&gt;
&lt;li&gt;making architectural decisions&lt;/li&gt;
&lt;li&gt;validating outcomes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The bottleneck is no longer coding speed.&lt;/p&gt;

&lt;p&gt;It is thinking.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Shift
&lt;/h2&gt;

&lt;p&gt;AI didn’t remove the need for engineers.&lt;/p&gt;

&lt;p&gt;It exposed something deeper:&lt;/p&gt;

&lt;p&gt;If you cannot clearly explain what you want to build,&lt;br&gt;
AI will build the wrong thing faster.&lt;/p&gt;

&lt;p&gt;But if you can think clearly,&lt;br&gt;
AI becomes a multiplier.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;The best engineers in this new world will not be the fastest coders.&lt;/p&gt;

&lt;p&gt;They will be the clearest thinkers.&lt;/p&gt;

&lt;p&gt;Because in the end, AI executes.&lt;/p&gt;

&lt;p&gt;Humans decide what is worth building.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>beginners</category>
      <category>coding</category>
    </item>
    <item>
      <title>Understanding SOLID Principles in Object-Oriented Programming</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Mon, 13 Apr 2026 14:08:02 +0000</pubDate>
      <link>https://dev.to/msnmongare/understanding-solid-principles-in-object-oriented-programming-1l4g</link>
      <guid>https://dev.to/msnmongare/understanding-solid-principles-in-object-oriented-programming-1l4g</guid>
      <description>&lt;p&gt;When building software, especially in object-oriented languages like Java, writing code that works is only part of the job. The real challenge is writing code that is easy to maintain, extend, and scale over time. This is where SOLID principles come in.&lt;/p&gt;

&lt;p&gt;SOLID is not about how many classes you should have. Instead, it is a set of five design principles that guide how you structure and organize your classes for better software quality.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is SOLID?
&lt;/h2&gt;

&lt;p&gt;SOLID is an acronym representing five key principles of object-oriented design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single Responsibility Principle&lt;/li&gt;
&lt;li&gt;Open/Closed Principle&lt;/li&gt;
&lt;li&gt;Liskov Substitution Principle&lt;/li&gt;
&lt;li&gt;Interface Segregation Principle&lt;/li&gt;
&lt;li&gt;Dependency Inversion Principle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These principles help developers reduce complexity, improve readability, and make systems easier to modify without breaking existing functionality.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Single Responsibility Principle (SRP)
&lt;/h2&gt;

&lt;p&gt;A class should have only one reason to change, meaning it should handle only one responsibility.&lt;/p&gt;

&lt;p&gt;For example, a class that manages user data should not also handle logging or database connections. Separating responsibilities ensures that changes in one area do not affect unrelated parts of the system.&lt;/p&gt;

&lt;p&gt;This makes your code easier to debug and maintain.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Open/Closed Principle (OCP)
&lt;/h2&gt;

&lt;p&gt;Software entities such as classes should be open for extension but closed for modification.&lt;/p&gt;

&lt;p&gt;This means you should be able to add new functionality without changing existing code. Instead of modifying a class directly, you extend it or use abstractions.&lt;/p&gt;

&lt;p&gt;This reduces the risk of introducing bugs into already tested code.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Liskov Substitution Principle (LSP)
&lt;/h2&gt;

&lt;p&gt;Objects of a subclass should be able to replace objects of the parent class without affecting the correctness of the program.&lt;/p&gt;

&lt;p&gt;In simple terms, if a class inherits from another, it should behave in a way that does not break expectations. If replacing a parent class with a child class causes errors, then the design violates this principle.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Interface Segregation Principle (ISP)
&lt;/h2&gt;

&lt;p&gt;Clients should not be forced to depend on interfaces they do not use.&lt;/p&gt;

&lt;p&gt;Instead of creating large, general-purpose interfaces, it is better to create smaller, more specific ones. This ensures that classes only implement what they actually need.&lt;/p&gt;

&lt;p&gt;This leads to cleaner and more focused code.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Dependency Inversion Principle (DIP)
&lt;/h2&gt;

&lt;p&gt;High-level modules should not depend on low-level modules. Both should depend on abstractions.&lt;/p&gt;

&lt;p&gt;This means you should rely on interfaces or abstract classes rather than concrete implementations. It reduces tight coupling between components and makes your system more flexible.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why SOLID Matters
&lt;/h2&gt;

&lt;p&gt;Applying SOLID principles leads to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better code organization&lt;/li&gt;
&lt;li&gt;Easier testing and debugging&lt;/li&gt;
&lt;li&gt;Improved scalability&lt;/li&gt;
&lt;li&gt;Reduced risk when making changes&lt;/li&gt;
&lt;li&gt;More reusable components&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;SOLID principles do not tell you how many classes to create. Instead, they guide how those classes should behave and interact with each other.&lt;/p&gt;

&lt;p&gt;By applying these principles consistently, you move from writing code that simply works to building systems that are robust, maintainable, and ready to grow with changing requirements.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>beginners</category>
      <category>python</category>
    </item>
    <item>
      <title>Understanding AI Agents: Autonomous vs Semi-Autonomous Systems</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Wed, 08 Apr 2026 12:21:22 +0000</pubDate>
      <link>https://dev.to/msnmongare/understanding-ai-agents-autonomous-vs-semi-autonomous-systems-4n3</link>
      <guid>https://dev.to/msnmongare/understanding-ai-agents-autonomous-vs-semi-autonomous-systems-4n3</guid>
      <description>&lt;p&gt;As Artificial Intelligence continues to evolve, the concept of an "agent" has become central to how intelligent systems are designed and deployed. From chatbots and virtual assistants to automated financial systems and robotics, agents are the building blocks behind many modern applications. To fully understand how these systems operate, it is important to start with a clear definition of what an AI agent is, and then explore the difference between autonomous and semi-autonomous agents.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is an AI Agent
&lt;/h2&gt;

&lt;p&gt;An AI agent is a software system or program that is designed to perceive its environment, make decisions, and take actions in order to achieve a specific goal.&lt;/p&gt;

&lt;p&gt;At its core, an agent has three main components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Perception&lt;/strong&gt;: The ability to gather information from its environment. This could be user input, API responses, sensor data, or database queries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decision-making&lt;/strong&gt;: The logic or intelligence that allows the agent to analyze information and determine what action to take.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action&lt;/strong&gt;: The execution of tasks, such as sending a message, updating a database, calling an API, or triggering another process.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple example is a chatbot. It receives a message from a user, processes the input, decides on a response, and sends a reply. More advanced agents can handle complex workflows, learn from data, and adapt over time.&lt;/p&gt;

&lt;p&gt;Agents can also be goal-driven, meaning they are designed not just to respond to inputs, but to actively pursue objectives. For instance, an agent might be tasked with increasing sales conversions, monitoring system health, or automating customer engagement.&lt;/p&gt;




&lt;h2&gt;
  
  
  Autonomous AI Agents
&lt;/h2&gt;

&lt;p&gt;Autonomous AI agents are systems that operate independently with minimal or no human intervention. Once they are given a goal or set of objectives, they can plan, make decisions, and execute actions on their own.&lt;/p&gt;

&lt;h3&gt;
  
  
  Characteristics of Autonomous Agents
&lt;/h3&gt;

&lt;p&gt;Autonomous agents are defined by a high level of independence. They can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make decisions without human approval&lt;/li&gt;
&lt;li&gt;Adapt to changing environments&lt;/li&gt;
&lt;li&gt;Continuously operate without supervision&lt;/li&gt;
&lt;li&gt;Optimize their actions based on feedback and data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These agents often rely on advanced techniques such as machine learning, reinforcement learning, and real-time data processing to improve their performance over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Examples of Autonomous Agents
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Automated trading systems that analyze markets and execute trades instantly&lt;/li&gt;
&lt;li&gt;Self-driving vehicles that navigate roads and respond to traffic conditions&lt;/li&gt;
&lt;li&gt;Network monitoring systems that detect and respond to threats without human input&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Advantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;High efficiency and speed&lt;/li&gt;
&lt;li&gt;Ability to operate at scale&lt;/li&gt;
&lt;li&gt;Reduced need for human labor in repetitive tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Challenges
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Higher risk due to lack of human oversight&lt;/li&gt;
&lt;li&gt;Difficulty in handling unexpected or ambiguous situations&lt;/li&gt;
&lt;li&gt;Ethical and accountability concerns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because of these challenges, fully autonomous systems are often deployed in controlled environments or where the risks are manageable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Semi-Autonomous AI Agents
&lt;/h2&gt;

&lt;p&gt;Semi-autonomous AI agents operate with a combination of machine intelligence and human involvement. They can perform many tasks independently, but they require human input, validation, or approval at key stages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Characteristics of Semi-Autonomous Agents
&lt;/h3&gt;

&lt;p&gt;These agents are designed to assist rather than fully replace human decision-making. They typically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provide recommendations or suggestions&lt;/li&gt;
&lt;li&gt;Execute tasks up to a certain point&lt;/li&gt;
&lt;li&gt;Require human confirmation for critical actions&lt;/li&gt;
&lt;li&gt;Work collaboratively with users&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Examples of Semi-Autonomous Agents
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Code assistants that suggest implementations while developers decide what to use&lt;/li&gt;
&lt;li&gt;Customer support systems that draft replies for human agents to review&lt;/li&gt;
&lt;li&gt;Financial systems that flag suspicious transactions for manual verification&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Advantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Lower risk due to human oversight&lt;/li&gt;
&lt;li&gt;Better handling of complex or sensitive scenarios&lt;/li&gt;
&lt;li&gt;Increased trust and accountability&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Challenges
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Slower than fully autonomous systems due to human involvement&lt;/li&gt;
&lt;li&gt;Requires well-designed interaction between human and machine&lt;/li&gt;
&lt;li&gt;May not scale as easily in high-demand environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Semi-autonomous systems are widely used in real-world applications because they strike a balance between efficiency and control.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Differences Between Autonomous and Semi-Autonomous Agents
&lt;/h2&gt;

&lt;p&gt;The distinction between these two types of agents lies primarily in control, decision-making, and risk management.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Autonomous Agents&lt;/th&gt;
&lt;th&gt;Semi-Autonomous Agents&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Human involvement&lt;/td&gt;
&lt;td&gt;Minimal or none&lt;/td&gt;
&lt;td&gt;Required at key points&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Decision-making&lt;/td&gt;
&lt;td&gt;Fully independent&lt;/td&gt;
&lt;td&gt;Shared with humans&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speed&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Moderate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Risk level&lt;/td&gt;
&lt;td&gt;Higher&lt;/td&gt;
&lt;td&gt;Lower&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use cases&lt;/td&gt;
&lt;td&gt;Automation at scale&lt;/td&gt;
&lt;td&gt;Human-assisted workflows&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  When to Use Each Type
&lt;/h2&gt;

&lt;p&gt;Choosing between autonomous and semi-autonomous agents depends on the context in which the system will operate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Autonomous Agents When:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Tasks are repetitive and well-defined&lt;/li&gt;
&lt;li&gt;Decisions are based on clear data patterns&lt;/li&gt;
&lt;li&gt;Speed and scalability are critical&lt;/li&gt;
&lt;li&gt;The risk of errors is low or manageable&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use Semi-Autonomous Agents When:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Decisions require human judgment or context&lt;/li&gt;
&lt;li&gt;The system operates in sensitive domains such as finance or healthcare&lt;/li&gt;
&lt;li&gt;Accountability and trust are important&lt;/li&gt;
&lt;li&gt;Errors could have significant consequences&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Practical Perspective for Developers
&lt;/h2&gt;

&lt;p&gt;For developers building systems such as APIs, backend services, or automation platforms, understanding this distinction is crucial.&lt;/p&gt;

&lt;p&gt;Autonomous agents are ideal for background processes such as data synchronization, automated notifications, or system monitoring. These tasks benefit from speed and do not usually require human validation.&lt;/p&gt;

&lt;p&gt;Semi-autonomous agents are better suited for user-facing features such as messaging platforms, financial transactions, or content generation tools. In these cases, allowing a human to review or approve actions helps prevent costly mistakes.&lt;/p&gt;

&lt;p&gt;For example, in a WhatsApp API platform, an autonomous agent might automatically send scheduled messages or respond to frequently asked questions. A semi-autonomous agent, on the other hand, might generate message drafts that a user reviews before sending to customers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;AI agents are powerful tools that enable systems to act intelligently and achieve goals with minimal manual effort. The distinction between autonomous and semi-autonomous agents lies in how much control is given to the system versus the human.&lt;/p&gt;

&lt;p&gt;Autonomous agents focus on independence, efficiency, and scalability, while semi-autonomous agents emphasize collaboration, oversight, and safety. Understanding when and how to use each type is essential for building reliable, effective, and responsible AI-driven systems.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>Testing M-PESA STK Push Callbacks Locally Without Exposing Your Server</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Wed, 18 Mar 2026 13:02:35 +0000</pubDate>
      <link>https://dev.to/msnmongare/testing-m-pesa-stk-push-callbacks-locally-without-exposing-your-server-pdo</link>
      <guid>https://dev.to/msnmongare/testing-m-pesa-stk-push-callbacks-locally-without-exposing-your-server-pdo</guid>
      <description>&lt;p&gt;If you've ever built an M-PESA STK Push integration, you've probably hit this wall: M-PESA needs a publicly accessible callback URL to send payment confirmations, but your app is running on localhost. How do you test this without deploying every single time?&lt;/p&gt;

&lt;p&gt;In this guide I'll show you a simple approach using webhook.site and a small Node.js poller script that automatically forwards M-PESA callbacks to your local machine.&lt;/p&gt;




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

&lt;p&gt;When a user completes an STK Push payment, M-PESA sends a POST request to your callback URL with the payment result. Normally that URL has to be publicly accessible. Since localhost isn't public, we'll use webhook.site as a middleman - it catches the callback from M-PESA, and our poller script picks it up and forwards it to our local app automatically.&lt;/p&gt;

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

&lt;p&gt;M-PESA → webhook.site → our poller script → localhost&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;Node.js installed on your machine&lt;/li&gt;
&lt;li&gt;Your Laravel app running locally&lt;/li&gt;
&lt;li&gt;An M-PESA Daraja API account (sandbox or production)&lt;/li&gt;
&lt;li&gt;Basic understanding of how STK Push works&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1 - Get a webhook.site URL
&lt;/h2&gt;

&lt;p&gt;Go to webhook.site and you'll immediately get a unique URL that 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;https://webhook.site/f5d500b7-1196-42ef-8ecd-c906e49f7df36
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The part after the last slash is your token. Copy it - you'll need it shortly. Keep this tab open so you can see incoming requests.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2 - Store the Callback URL in Your .env
&lt;/h2&gt;

&lt;p&gt;Instead of hardcoding the webhook.site URL in your code, store it in your .env file so you can swap it out easily without touching any code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;MPESA_CALLBACK_URL&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;https://webhook.site/f5d500b7-1196-42ef-8ecd-c906e49f7df36&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On production you simply leave this variable out and it falls back to your real callback URL automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3 - Update Your ProcessController
&lt;/h2&gt;

&lt;p&gt;In your M-PESA ProcessController where you build the STK Push request, find the CallBackURL line and update it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="s2"&gt;"CallBackURL"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'MPESA_CALLBACK_URL'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ipn.MPesa'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this does is simple. If MPESA_CALLBACK_URL is set in your .env it uses that. If it's not set, it falls back to your real route. This means locally it points to webhook.site, and on production it uses your actual callback route - no code changes needed when you deploy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4 - Create the Poller Script
&lt;/h2&gt;

&lt;p&gt;This is the magic piece. Create a file called forwarder.js anywhere on your machine - I put mine in the project root. Paste this in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;WEBHOOK_TOKEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;f5d500b7-1196-42ef-8ecd-c906e49f7df36&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// your token here&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LOCAL_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:8000/ipn/mpesa&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;POLL_INTERVAL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// check every 5 seconds&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;lastSeenId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;poll&lt;/span&gt;&lt;span class="p"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://webhook.site/token/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;WEBHOOK_TOKEN&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/requests?sorting=newest&amp;amp;per_page=1`&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;data&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;res&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="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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="k"&gt;return&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;latest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;latest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;lastSeenId&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="c1"&gt;// already processed this one&lt;/span&gt;

        &lt;span class="nx"&gt;lastSeenId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;latest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uuid&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;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;latest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&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;New callback received! Forwarding to local app...&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;body&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;forward&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;LOCAL_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;body&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;Done. Local app responded with status:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&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;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;poll&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;POLL_INTERVAL&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;Watching webhook.site for incoming callbacks...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace the WEBHOOK_TOKEN value with your own token from Step 1. The LOCAL_URL should match whatever route your app uses for the M-PESA IPN.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5 - Run Everything
&lt;/h2&gt;

&lt;p&gt;You'll need two terminals open.&lt;/p&gt;

&lt;p&gt;Terminal 1 - start your Laravel app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Terminal 2 - start the poller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node forwarder.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see this message in Terminal 2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Watching webhook.site for incoming callbacks...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 6 - Trigger a Test Payment
&lt;/h2&gt;

&lt;p&gt;Go through your app's deposit flow and trigger an STK Push. Enter your phone number and hit pay. You'll get the prompt on your phone.&lt;/p&gt;

&lt;p&gt;Once you complete the payment, here's what happens automatically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;M-PESA sends the callback to webhook.site&lt;/li&gt;
&lt;li&gt;Your poller picks it up within 5 seconds&lt;/li&gt;
&lt;li&gt;It forwards the full payload to your local app&lt;/li&gt;
&lt;li&gt;Your local app processes it and updates the database&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You'll see something like this in Terminal 2:&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="err"&gt;New&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;callback&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;received!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Forwarding&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;app...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"Body"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"stkCallback"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"ResultCode"&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="nl"&gt;"CheckoutRequestID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"ws_CO_xxxxx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;Done.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;responded&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;status:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And your database gets updated just like it would in production.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Not Just Use ngrok?
&lt;/h2&gt;

&lt;p&gt;Ngrok is the most common answer to this problem and it works well. However if you're running a licensed script, adding a new public URL can trigger a second domain detection and invalidate your license. The webhook.site approach avoids this completely since your app never gets a new public URL - only the callback endpoint changes temporarily, and only in your local .env.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cleaning Up After Testing
&lt;/h2&gt;

&lt;p&gt;When you're done testing, remove MPESA_CALLBACK_URL from your .env or comment it out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# MPESA_CALLBACK_URL=https://webhook.site/f5d500b7-1196-42ef-8ecd-c906e49f7df36
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your app will fall back to the real route automatically. Stop the forwarder.js script and you're done. Nothing was changed in your codebase, nothing gets pushed to production.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The whole setup takes about 5 minutes and gives you a proper local testing loop for M-PESA callbacks without ngrok, without deploying, and without touching your production environment. The poller script is lightweight, requires no dependencies beyond Node.js, and you can reuse the same approach for any other payment gateway callback - just change the LOCAL_URL to point to the right IPN route.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>programming</category>
      <category>ai</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to Run Multiple PHP Versions on Windows and Switch Easily</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Sun, 15 Mar 2026 12:45:08 +0000</pubDate>
      <link>https://dev.to/msnmongare/how-to-run-multiple-php-versions-on-windows-and-switch-easily-a7k</link>
      <guid>https://dev.to/msnmongare/how-to-run-multiple-php-versions-on-windows-and-switch-easily-a7k</guid>
      <description>&lt;p&gt;When working on modern PHP projects like those built with &lt;strong&gt;Laravel&lt;/strong&gt;, you will eventually run into version conflicts.&lt;/p&gt;

&lt;p&gt;One project may require &lt;strong&gt;PHP 8.3&lt;/strong&gt;, while another older project may still run on &lt;strong&gt;PHP 8.1 or 8.2&lt;/strong&gt;. If your system only has one PHP version installed, you will constantly run into errors like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Composer detected issues in your platform:
Your Composer dependencies require a PHP version "&amp;gt;= 8.3.0".
You are running 8.2.12.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of reinstalling PHP every time you switch projects, the better solution is to install &lt;strong&gt;multiple PHP versions&lt;/strong&gt; and switch between them instantly.&lt;/p&gt;

&lt;p&gt;One of the easiest ways to achieve this on Windows is by using &lt;strong&gt;Scoop&lt;/strong&gt;, a lightweight package manager designed for developers.&lt;/p&gt;

&lt;p&gt;Below is a simple and clean setup.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Install Scoop
&lt;/h2&gt;

&lt;p&gt;Open &lt;strong&gt;PowerShell&lt;/strong&gt; and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Set-ExecutionPolicy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;RemoteSigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Scope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;CurrentUser&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;irm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;get.scoop.sh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirm installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;scoop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--version&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scoop installs tools into your user directory and avoids the common permission problems that occur with traditional installers.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Add the Versions Bucket
&lt;/h2&gt;

&lt;p&gt;Scoop organizes packages using "buckets". To install multiple PHP versions, you need the versions bucket.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;scoop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;versions&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This bucket contains packages like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;php81&lt;/li&gt;
&lt;li&gt;php82&lt;/li&gt;
&lt;li&gt;php83&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Install Multiple PHP Versions
&lt;/h2&gt;

&lt;p&gt;You can now install any PHP version you need.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;scoop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;php82&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;scoop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;php83&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check installed packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;scoop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point both PHP versions exist on your system, but only one will be active at a time.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Switch Between PHP Versions
&lt;/h2&gt;

&lt;p&gt;To switch to &lt;strong&gt;PHP 8.3&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;scoop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;php83&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To switch to &lt;strong&gt;PHP 8.2&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;scoop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;php82&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the active version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;php&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-v&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This instantly updates your terminal environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Running Your Laravel Project
&lt;/h2&gt;

&lt;p&gt;Once the correct version is active, you can start your project normally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the project requires PHP 8.3, switching to php83 will resolve the platform check error.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Approach Is Powerful
&lt;/h2&gt;

&lt;p&gt;Modern development environments rarely use a single runtime version.&lt;/p&gt;

&lt;p&gt;You may have projects such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Legacy project running PHP 8.1&lt;/li&gt;
&lt;li&gt;Production system using PHP 8.2&lt;/li&gt;
&lt;li&gt;New &lt;strong&gt;Laravel 11&lt;/strong&gt; applications requiring PHP 8.3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using Scoop allows you to move between these environments instantly without reinstalling anything.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bonus: Install Other Developer Tools
&lt;/h2&gt;

&lt;p&gt;Scoop can also install common development tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;scoop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;composer&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;scoop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;nodejs&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;scoop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;git&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a clean development environment that is easy to maintain and update.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Managing multiple PHP versions used to be complicated on Windows. With &lt;strong&gt;Scoop&lt;/strong&gt;, the process becomes simple and developer friendly.&lt;/p&gt;

&lt;p&gt;Instead of fighting your environment, you can focus on building applications and switching versions only when necessary.&lt;/p&gt;

&lt;p&gt;For backend developers working with frameworks like &lt;strong&gt;Laravel&lt;/strong&gt;, this setup saves time and prevents version related errors during development.&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>M-PESA DARAJA API - C2B Integration Guide</title>
      <dc:creator>Sospeter Mong'are</dc:creator>
      <pubDate>Fri, 13 Mar 2026 09:42:47 +0000</pubDate>
      <link>https://dev.to/msnmongare/m-pesa-daraja-api-c2b-integration-guide-djn</link>
      <guid>https://dev.to/msnmongare/m-pesa-daraja-api-c2b-integration-guide-djn</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;If you are building a web or mobile app that needs to receive payments from M-PESA customers in Kenya, the Safaricom Daraja API is the tool you need. This guide will walk you through everything from scratch  no prior API experience required.&lt;/p&gt;

&lt;p&gt;By the end of this guide, you will understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What the C2B API is and how it works&lt;/li&gt;
&lt;li&gt;How to set up and test it in the sandbox using Postman&lt;/li&gt;
&lt;li&gt;What ValidationURL and ConfirmationURL do&lt;/li&gt;
&lt;li&gt;How to take your integration live&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What is C2B?
&lt;/h2&gt;

&lt;p&gt;C2B stands for &lt;strong&gt;Customer to Business&lt;/strong&gt;. It refers to the flow of money from an individual customer to your business. When a customer pays your Paybill or Till number via M-PESA, that is a C2B transaction.&lt;/p&gt;

&lt;p&gt;The C2B API allows your application to receive real-time notifications every time a payment comes in, so you can automate things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Marking an order as paid&lt;/li&gt;
&lt;li&gt;Sending a payment receipt to the customer&lt;/li&gt;
&lt;li&gt;Updating your accounting system automatically&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What is Daraja?
&lt;/h2&gt;

&lt;p&gt;Daraja (which means "bridge" in Swahili) is Safaricom's developer portal that gives you access to the M-PESA API. It provides a &lt;strong&gt;sandbox&lt;/strong&gt; (testing) environment where you can simulate payments without using real money, and a &lt;strong&gt;production&lt;/strong&gt; environment for real transactions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Portal URL:&lt;/strong&gt; &lt;a href="https://developer.safaricom.co.ke" rel="noopener noreferrer"&gt;https://developer.safaricom.co.ke&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  PART ONE: SANDBOX TESTING
&lt;/h2&gt;

&lt;p&gt;Before using real money, Daraja gives you a sandbox environment to safely test your integration. This is where you should always start.&lt;/p&gt;




&lt;h2&gt;
  
  
  STEP 1 - Create a Daraja Account &amp;amp; App
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Set up your developer profile&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://developer.safaricom.co.ke" rel="noopener noreferrer"&gt;https://developer.safaricom.co.ke&lt;/a&gt; and click &lt;strong&gt;Sign Up&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Fill in your details and verify your email address&lt;/li&gt;
&lt;li&gt;Once logged in, click &lt;strong&gt;"My Apps"&lt;/strong&gt; in the top navigation menu&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;"Create Sandbox App"&lt;/strong&gt; - give it any name (e.g. "MyShopC2B")&lt;/li&gt;
&lt;li&gt;Under the products section, check &lt;strong&gt;"M-Pesa Sandbox"&lt;/strong&gt; to enable sandbox APIs&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;"Create App"&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your app will now appear in the "My Apps" section. Click on it and you will see two important values:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Key&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Consumer Key&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Acts like your app's username for the API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Consumer Secret&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Acts like your app's password for the API&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💾 &lt;strong&gt;Save These!&lt;/strong&gt; Copy your Consumer Key and Consumer Secret somewhere safe. You will need them for every API call.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  STEP 2 - Get an Access Token
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Authenticate before making any API call&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Every single API request to Daraja requires an access token. Think of it like a &lt;strong&gt;temporary password&lt;/strong&gt; that proves your identity. It expires after 1 hour and you need to generate a new one.&lt;/p&gt;

&lt;h3&gt;
  
  
  In Postman:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Method:&lt;/strong&gt; &lt;code&gt;GET&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;URL:&lt;/strong&gt; &lt;code&gt;https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Go to the &lt;strong&gt;Authorization&lt;/strong&gt; tab in Postman:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auth Type: &lt;strong&gt;Basic Auth&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Username: paste your &lt;strong&gt;Consumer Key&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Password: paste your &lt;strong&gt;Consumer Secret&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Send&lt;/strong&gt;. You should get a response like this:&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;"access_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SGWcJPtNtYNPGm1DqBNqZZZZZZ"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expires_in"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3599"&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;blockquote&gt;
&lt;p&gt;📋 &lt;strong&gt;Copy It!&lt;/strong&gt; Copy the &lt;code&gt;access_token&lt;/code&gt; value. You will paste it in the Authorization header of every request below.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  STEP 3 - Understanding Your Callback URLs
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;The heart of how C2B notifications work&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Before registering your URLs, it is important to understand what each one does. When a customer makes a payment, Safaricom contacts your server at two different points in the payment flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;📱 Customer Pays  →  🔍 ValidationURL  →  ✅ ConfirmationURL
  (Sends money)      ("Should I allow    ("Payment done,
                          this?")          record it!")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  ValidationURL - "Should I allow this payment?"
&lt;/h3&gt;

&lt;p&gt;This is called &lt;strong&gt;before&lt;/strong&gt; the payment is processed. Safaricom sends you the payment details and waits for your response. You can accept or reject the payment based on your own business logic - for example, checking if the account number the customer entered actually exists in your system.&lt;/p&gt;

&lt;p&gt;You respond with one of the following:&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="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Accept&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;payment&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;"ResultCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;"ResultDesc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Accepted"&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="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Reject&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;payment&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;"ResultCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C2B00012"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ResultDesc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Rejected"&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;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Note:&lt;/strong&gt; ValidationURL is &lt;strong&gt;optional&lt;/strong&gt;. If you set &lt;code&gt;ResponseType&lt;/code&gt; to &lt;code&gt;"Completed"&lt;/code&gt; when registering, Safaricom skips validation and auto-accepts all payments. This is recommended for beginners.&lt;br&gt;
Validation URL is the URL that receives the validation request from API upon payment submission. The validation URL is only called if external validation on the registered short code is enabled.&lt;br&gt;
(By default, External Validation is disabled)&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  ConfirmationURL - "Payment went through, save the details"
&lt;/h3&gt;

&lt;p&gt;This is called &lt;strong&gt;after&lt;/strong&gt; the payment has been successfully processed. At this point, you &lt;strong&gt;cannot&lt;/strong&gt; reject the payment - it has already gone through. This is where your app should record the transaction, update a database, send a receipt, or trigger any other business logic.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;ValidationURL&lt;/th&gt;
&lt;th&gt;ConfirmationURL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;When called&lt;/td&gt;
&lt;td&gt;BEFORE payment is processed&lt;/td&gt;
&lt;td&gt;AFTER payment is processed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Purpose&lt;/td&gt;
&lt;td&gt;Accept or reject the payment&lt;/td&gt;
&lt;td&gt;Record and act on the payment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Your response matters?&lt;/td&gt;
&lt;td&gt;YES - can block the payment&lt;/td&gt;
&lt;td&gt;NO - informational only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Required?&lt;/td&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;td&gt;Yes, always required&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🏪 &lt;strong&gt;Real-world analogy:&lt;/strong&gt; Think of it like a supermarket checkout. The ValidationURL is the cashier checking if your loyalty card is valid before ringing up. The ConfirmationURL is the receipt printer - it just records that the sale happened.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  STEP 4 - Get a Free Callback URL for Testing
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Use webhook.site to capture callbacks&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Your ValidationURL and ConfirmationURL must be &lt;strong&gt;publicly accessible HTTPS endpoints&lt;/strong&gt;. For sandbox testing, you do not need a real server - you can use a free tool called &lt;strong&gt;webhook.site&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://webhook.site" rel="noopener noreferrer"&gt;https://webhook.site&lt;/a&gt; in your browser&lt;/li&gt;
&lt;li&gt;You will instantly get a unique URL like: &lt;code&gt;https://webhook.site/abc-123-xyz&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Copy that URL - you will use it as both your ValidationURL and ConfirmationURL for testing&lt;/li&gt;
&lt;li&gt;Leave the webhook.site tab open. Callbacks from Safaricom will appear here in real time&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;🚫 &lt;strong&gt;URL Rules:&lt;/strong&gt; Never use words like "MPesa", "M-Pesa", or "Safaricom" in your URLs - the system will block them. Also, localhost URLs will not work. Always use a proper HTTPS URL.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  STEP 5 - Register Your Callback URLs
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Tell Safaricom where to send payment notifications&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now you will call the Register URL API to link your callback URLs to your Paybill shortcode.&lt;/p&gt;

&lt;h3&gt;
  
  
  In Postman:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Method:&lt;/strong&gt; &lt;code&gt;POST&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;URL:&lt;/strong&gt; &lt;code&gt;https://sandbox.safaricom.co.ke/mpesa/c2b/v1/registerurl&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Headers tab - add these two headers:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Header Key&lt;/th&gt;
&lt;th&gt;Header Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Authorization&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Bearer YOUR_ACCESS_TOKEN&lt;/code&gt; (paste the token from Step 2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Content-Type&lt;/td&gt;
&lt;td&gt;&lt;code&gt;application/json&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Body tab - select "raw" and "JSON", then paste:&lt;/strong&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;"ShortCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"600584"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ResponseType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Completed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Either&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Cancelled&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Completed&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ConfirmationURL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://webhook.site/your-unique-url"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ValidationURL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://webhook.site/your-unique-url"&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;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;Explanation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ShortCode&lt;/td&gt;
&lt;td&gt;The sandbox test Paybill number (600584 is the default sandbox shortcode)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ResponseType&lt;/td&gt;
&lt;td&gt;"Completed" means skip validation and auto-accept all payments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ConfirmationURL&lt;/td&gt;
&lt;td&gt;Your webhook.site URL - where Safaricom sends payment confirmations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ValidationURL&lt;/td&gt;
&lt;td&gt;Your webhook.site URL - where Safaricom asks for approval (optional here)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A successful response looks like:&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;"OriginatorCoversationID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;"ResponseCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;"ResponseDescription"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"success"&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;h2&gt;
  
  
  STEP 6 - Simulate a C2B Payment
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Pretend a customer is paying you&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now for the fun part - you will simulate a customer sending money to your Paybill. Safaricom provides a test phone number you can use.&lt;/p&gt;

&lt;h3&gt;
  
  
  In Postman:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Method:&lt;/strong&gt; &lt;code&gt;POST&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;URL:&lt;/strong&gt; &lt;code&gt;https://sandbox.safaricom.co.ke/mpesa/c2b/v1/simulate&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use the same Authorization header as before, then paste this JSON body:&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;"ShortCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"600584"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"CommandID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CustomerPayBillOnline"&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="s2"&gt;"100"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Msisdn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"254708374149"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"BillRefNumber"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"INV001"&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;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;Explanation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ShortCode&lt;/td&gt;
&lt;td&gt;Your sandbox Paybill number&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CommandID&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;"CustomerPayBillOnline"&lt;/code&gt; for Paybill, or &lt;code&gt;"CustomerBuyGoodsOnline"&lt;/code&gt; for Till&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amount&lt;/td&gt;
&lt;td&gt;The amount the simulated customer is paying (in KES)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Msisdn&lt;/td&gt;
&lt;td&gt;The test customer phone number - always use &lt;code&gt;254708374149&lt;/code&gt; in sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BillRefNumber&lt;/td&gt;
&lt;td&gt;The account reference - e.g. an invoice number or order ID&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  STEP 7 - Check Your Callback
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;See what your app would receive&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After sending the simulation request, go to your webhook.site tab. Within a few seconds, you should see a POST request arrive. This is exactly the payload your real server would receive when a customer pays.&lt;/p&gt;

&lt;p&gt;It will look something like this:&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;"TransactionType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Pay Bill"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"TransID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UCB030CBG1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"TransTime"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"20260311161727"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"TransAmount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"BusinessShortCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"600991"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"BillRefNumber"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"account001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"InvoiceNumber"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;"OrgAccountBalance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4635316.60"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"ThirdPartyTransID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;"MSISDN"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bbff37cea44ac0b2d964ee0dfb8d2df8513dc7ba1b36129a929fc3fbd6dd4af4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"FirstName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John"&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;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;Value&lt;/th&gt;
&lt;th&gt;Explanation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TransactionType&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Pay Bill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The type of transaction. Will be "Pay Bill" for Paybill or "Buy Goods" for Till numbers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TransID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UCB030CBG1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unique M-PESA transaction ID. Use this as your reference to avoid processing the same payment twice&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TransTime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;20260311161727&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Timestamp of the transaction in &lt;code&gt;YYYYMMDDHHmmss&lt;/code&gt; format. This one means 11 March 2026 at 16:17:27&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TransAmount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1.00&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The amount the customer paid in KES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BusinessShortCode&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;600991&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Your Paybill or Till number that received the payment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BillRefNumber&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;account001&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The account number the customer entered when paying. Use this to identify which customer or order the payment belongs to&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;InvoiceNumber&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;(empty)&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Optional invoice number. Usually empty for most C2B transactions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OrgAccountBalance&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;4635316.60&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Your Paybill account balance after this transaction was processed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ThirdPartyTransID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;(empty)&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Used in some integrations for a third-party reference. Usually empty&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MSISDN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bbff37c...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The customer's phone number. In sandbox it is returned as a hashed/masked value for privacy.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FirstName&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;John&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The first name of the customer as registered on M-PESA&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Tip:&lt;/strong&gt; The three most important fields to save in your database are &lt;code&gt;TransID&lt;/code&gt; (to prevent duplicate processing), &lt;code&gt;BillRefNumber&lt;/code&gt; (to identify the customer/order), and &lt;code&gt;TransAmount&lt;/code&gt; (to confirm the correct amount was paid).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In a real application, you would read this JSON payload and use the &lt;code&gt;TransAmount&lt;/code&gt;, &lt;code&gt;BillRefNumber&lt;/code&gt;, and &lt;code&gt;MSISDN&lt;/code&gt; fields to update your database and send a receipt to the customer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Sandbox Note:&lt;/strong&gt; Sandbox callbacks can sometimes be unreliable. If your webhook.site does not receive anything after 30 seconds, try the simulation again. This is a known sandbox issue.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Full C2B Flow Recap
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;What Happens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Get Token&lt;/td&gt;
&lt;td&gt;You authenticate with your Consumer Key &amp;amp; Secret and receive a temporary access token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Register URLs&lt;/td&gt;
&lt;td&gt;You tell Safaricom where to send payment notifications&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Customer Pays&lt;/td&gt;
&lt;td&gt;A real or simulated customer sends money to your Paybill or Till number&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Validation&lt;/td&gt;
&lt;td&gt;Safaricom hits your ValidationURL asking "should I accept this?" (optional)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Confirmation&lt;/td&gt;
&lt;td&gt;Safaricom hits your ConfirmationURL with the full payment details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Your App Acts&lt;/td&gt;
&lt;td&gt;Your server reads the payload and updates the database, sends a receipt, etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  PART TWO: GOING LIVE
&lt;/h2&gt;

&lt;p&gt;Once your sandbox integration is working correctly, you are ready to go live and process real M-PESA transactions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites Before Going Live
&lt;/h2&gt;

&lt;p&gt;Make sure you have all of the following before applying for live credentials:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A working sandbox integration - all steps above tested and confirmed working&lt;/li&gt;
&lt;li&gt;A registered and verified Paybill or Till Number from Safaricom&lt;/li&gt;
&lt;li&gt;An active Safaricom G2 Business Admin account (used to verify your shortcode)&lt;/li&gt;
&lt;li&gt;A real publicly accessible HTTPS server URL (not localhost, not webhook.site) for your callback URLs&lt;/li&gt;
&lt;li&gt;Company documentation: Certificate of incorporation, KRA PIN, and directors' IDs (for businesses)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;🏢 &lt;strong&gt;Get a Paybill First:&lt;/strong&gt; To get a Paybill number, visit a Safaricom shop with your company registration documents. For a Till number, you can apply via the Safaricom self-onboarding portal. Processing takes 24–72 hours.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 1 - Register on the Safaricom G2 Portal
&lt;/h2&gt;

&lt;p&gt;The G2 portal (&lt;a href="https://org.ke.mpesa.com" rel="noopener noreferrer"&gt;https://org.ke.mpesa.com&lt;/a&gt;) is Safaricom's business management platform. You need an account here so Daraja can verify you own the Paybill or Till number you want to use.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Send an email to &lt;strong&gt;&lt;a href="mailto:M-PESABusiness@safaricom.co.ke"&gt;M-PESABusiness@safaricom.co.ke&lt;/a&gt;&lt;/strong&gt; requesting a Business Admin account&lt;/li&gt;
&lt;li&gt;Attach required documents: company registration certificate, KRA PIN, directors' IDs, and a signed board resolution&lt;/li&gt;
&lt;li&gt;Safaricom will create your G2 account and send login credentials&lt;/li&gt;
&lt;li&gt;Log in, change your password, and create an "assistant role" user - these credentials will be used in the next step&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 2 - Confirm Your Sandbox Integration is Solid
&lt;/h2&gt;

&lt;p&gt;Before applying to go live, make sure all of the following are working in sandbox:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access token generation is working&lt;/li&gt;
&lt;li&gt;URL registration returns a success response&lt;/li&gt;
&lt;li&gt;Simulated payments are triggering callbacks to your server&lt;/li&gt;
&lt;li&gt;Your server is responding with the correct &lt;code&gt;200 OK&lt;/code&gt; responses&lt;/li&gt;
&lt;li&gt;Your database or backend is correctly parsing and saving the callback payload&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 3 - Click "Go Live" on the Daraja Portal
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Log into &lt;a href="https://developer.safaricom.co.ke" rel="noopener noreferrer"&gt;https://developer.safaricom.co.ke&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;"My Apps"&lt;/strong&gt; and open your app&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;"Go Live"&lt;/strong&gt; button&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;"Verification by Shortcode"&lt;/strong&gt; as the method&lt;/li&gt;
&lt;li&gt;Enter your Paybill/Till shortcode and your G2 assistant user credentials&lt;/li&gt;
&lt;li&gt;Select the API products you need (e.g. C2B)&lt;/li&gt;
&lt;li&gt;Upload your &lt;strong&gt;test cases&lt;/strong&gt; - a document showing what you tested and the results&lt;/li&gt;
&lt;li&gt;Submit your request&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;⏱️ &lt;strong&gt;Approval Time:&lt;/strong&gt; Safaricom typically takes 24–72 hours to review and approve your live request. You will receive your production credentials by email once approved.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 4 - Replace Sandbox Credentials with Live Ones
&lt;/h2&gt;

&lt;p&gt;Once approved, update your application to use production values:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What to Change&lt;/th&gt;
&lt;th&gt;Sandbox → Production&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;API Base URL&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;sandbox.safaricom.co.ke&lt;/code&gt; → &lt;code&gt;api.safaricom.co.ke&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer Key&lt;/td&gt;
&lt;td&gt;Sandbox key → New live Consumer Key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer Secret&lt;/td&gt;
&lt;td&gt;Sandbox secret → New live Consumer Secret&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ShortCode&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;600584&lt;/code&gt; (test) → Your actual Paybill/Till number&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Callback URLs&lt;/td&gt;
&lt;td&gt;webhook.site URLs → Your real HTTPS server URLs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Step 5 - Test a Small Live Transaction
&lt;/h2&gt;

&lt;p&gt;After switching to production credentials, do a small test with real money (e.g. KES 1) to confirm everything works end to end:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pay your Paybill from a real M-PESA number&lt;/li&gt;
&lt;li&gt;Confirm your ConfirmationURL receives the callback&lt;/li&gt;
&lt;li&gt;Verify your database is updated correctly&lt;/li&gt;
&lt;li&gt;Check that the customer receives the expected response or receipt&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 &lt;strong&gt;Keep Logs:&lt;/strong&gt; Always log every transaction in the early stages of going live. Implement retry logic in case of failed callbacks - Safaricom may occasionally retry if your server is slow to respond.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Common Mistakes to Avoid
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;❌ Do NOT do this&lt;/th&gt;
&lt;th&gt;✅ Do this instead&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Include "MPesa" or "Safaricom" in your callback URLs&lt;/td&gt;
&lt;td&gt;Use neutral words in your URLs e.g. &lt;code&gt;/payment/confirm&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Use localhost or 127.0.0.1 as your callback URL&lt;/td&gt;
&lt;td&gt;Host your app or use a tunneling tool like Ngrok (sandbox only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Use webhook.site/ngrok URLs in production&lt;/td&gt;
&lt;td&gt;Use a real, stable HTTPS server in production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Apply for all products without knowing what you need&lt;/td&gt;
&lt;td&gt;Plan ahead - decide which APIs you need before going live&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Forget to renew your access token every hour&lt;/td&gt;
&lt;td&gt;Generate a fresh token on each session or add auto-renewal logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Ignore the callback response format&lt;/td&gt;
&lt;td&gt;Always respond with &lt;code&gt;200 OK&lt;/code&gt; and valid &lt;code&gt;ResultCode&lt;/code&gt;/&lt;code&gt;ResultDesc&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Useful Resources
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;URL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Daraja Developer Portal&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developer.safaricom.co.ke" rel="noopener noreferrer"&gt;https://developer.safaricom.co.ke&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Daraja API Documentation&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developer.safaricom.co.ke/APIs" rel="noopener noreferrer"&gt;https://developer.safaricom.co.ke/APIs&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Safaricom G2 Business Portal&lt;/td&gt;
&lt;td&gt;&lt;a href="https://org.ke.mpesa.com" rel="noopener noreferrer"&gt;https://org.ke.mpesa.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test Callback Tool&lt;/td&gt;
&lt;td&gt;&lt;a href="https://webhook.site" rel="noopener noreferrer"&gt;https://webhook.site&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;M-PESA Business Email&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:M-PESABusiness@safaricom.co.ke"&gt;M-PESABusiness@safaricom.co.ke&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;If you need payment integrations, You can reach out to me on &lt;a href="mailto:sosmongare@gmail"&gt;email&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy building!&lt;/p&gt;

</description>
      <category>mpesa</category>
      <category>api</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
