<?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: Simangaliso Vilakazi</title>
    <description>The latest articles on DEV Community by Simangaliso Vilakazi (@smngvlkz).</description>
    <link>https://dev.to/smngvlkz</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1080878%2Fda94027c-c16f-4fc8-9f3b-46a5f916d696.png</url>
      <title>DEV Community: Simangaliso Vilakazi</title>
      <link>https://dev.to/smngvlkz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/smngvlkz"/>
    <language>en</language>
    <item>
      <title>I built a Zapier integration for my solo SaaS. Here's every decision I made</title>
      <dc:creator>Simangaliso Vilakazi</dc:creator>
      <pubDate>Mon, 06 Apr 2026 11:05:33 +0000</pubDate>
      <link>https://dev.to/smngvlkz/i-built-a-zapier-integration-for-my-solo-saas-heres-every-decision-i-made-2fm1</link>
      <guid>https://dev.to/smngvlkz/i-built-a-zapier-integration-for-my-solo-saas-heres-every-decision-i-made-2fm1</guid>
      <description>&lt;p&gt;I am a full-stack software engineer in Durban, South Africa. I work  full-time and build SaaS products after hours. PayChasers is one of them. It's a payment chasing tool for anyone who's owed money and tired of asking twice.&lt;/p&gt;

&lt;p&gt;Today I submitted it to Zapier's partner program. Here's exactly how it went and what I would do differently.&lt;/p&gt;

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

&lt;p&gt;PayChasers handles chasing payment reminders for invoices, rent, deposits, failed payments, and personal debts — anything where someone owes you money and you'd rather not send awkward reminders yourself.&lt;/p&gt;

&lt;p&gt;You add a chase. You choose how to handle it. Send a follow-up manually when you're ready, or turn on Auto Chase and let PayChasers handle the escalation for you: friendly nudge, due today, overdue, firm. Email or WhatsApp. The tone sharpens as the payment ages. You stay in control of every message. Nothing goes out without your templates, your tone, your timing.&lt;/p&gt;

&lt;p&gt;It works. But the product lives in isolation. If someone tracks invoices in Google Sheets, manages deals in HubSpot, or processes payments through Stripe, they have to manually create chases in PayChasers. That's friction. Friction kills adoption.&lt;/p&gt;

&lt;p&gt;So I built a Zapier integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Zapier and not direct integrations
&lt;/h2&gt;

&lt;p&gt;I considered building native integrations. A Stripe webhook listener, a Google Sheets sync, a HubSpot connector. Each one would take days. Each one adds a maintenance surface. Each one serves one app.&lt;/p&gt;

&lt;p&gt;Zapier serves 7000+ apps with one integration.&lt;/p&gt;

&lt;p&gt;I built one API surface and let Zapier be the bridge. A user who wants "Hubspot deal closes -&amp;gt; create a chase in PayChasers" can build that in 2 minutes without me writing a single line of HubSpot code.&lt;/p&gt;

&lt;p&gt;The trade-off is control. I can't fine-tune each experience. But for a solo engineer, the leverage is hard to beat.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the integration exposes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Triggers (things that happen in PayChasers)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;New Chase&lt;/strong&gt; - fires when a user creates a chase&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chase Overdue&lt;/strong&gt; - fires when a chase passes its due date&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chase Paid&lt;/strong&gt; - fires when a chase is marked as paid&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Follow-up Sent&lt;/strong&gt; - fires when an automated follow-up email goes out&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Actions (things Zapier can do in PayChasers)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Create Chase&lt;/strong&gt; - create a new chase with client details, amount, due date, currency, and chase type&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Send Follow-up&lt;/strong&gt; - trigger a follow-up on an existing chase&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mark as Paid&lt;/strong&gt; - mark a chase as paid&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Auth
&lt;/h3&gt;

&lt;p&gt;API key authentication. Users generate a key in Settings, paste it into Zapier. Simple, no OAuth dance. The key is hashed with SHA-256 before storage. I never see the raw key after it's generated.&lt;/p&gt;

&lt;h2&gt;
  
  
  The API layer
&lt;/h2&gt;

&lt;p&gt;I built a &lt;code&gt;/api/v1/&lt;/code&gt; REST API specifically for this. It's separate from the internal app routes. Endpoints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET    /api/v1/me                   — verify auth
GET    /api/v1/chases               — list chases
POST   /api/v1/chases               — create a chase
POST   /api/v1/chases/:id/paid      — mark paid
POST   /api/v1/chases/:id/follow-up — send follow-up
GET    /api/v1/clients              — list clients
GET    /api/v1/triggers/*           — polling triggers for Zapier
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The triggers use Zapier's polling model. Zapier hits the trigger endpoint every few minutes, and I return items sorted by most recent. Zapier deduplicates by ID.&lt;/p&gt;

&lt;p&gt;I considered webhooks (Zapier's REST Hook model) but polling was simpler to implement and doesn't require me to manage subscription state. For a product at my scale, polling every few minutes is fine and I dont see any issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  The embed strategy
&lt;/h2&gt;

&lt;p&gt;Zapier offers a "Full Zapier Experience" embed. A JavaScript widget that lets users discover and build "Zaps" directly inside your app. I built the page for it but designed it with two states:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before approval:&lt;/strong&gt; An informational page explaining whats coming. no dead UI, no broken embed. Just a clear explanation of what Zapier automation enables and what it will look like.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After approval:&lt;/strong&gt; Set one environment variable (&lt;code&gt;NEXT_PUBLIC_ZAPIER_FZE_CLIENT_ID&lt;/code&gt;), ready to redeploy and the embed loads. Users can browse Zap templates and build automations without leaving PayChasers.&lt;/p&gt;

&lt;p&gt;Zero code changes needed when the switch flips. One env var.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ref-based signup attribution
&lt;/h2&gt;

&lt;p&gt;This is the part I almost skipped and I am glad I didn't.&lt;/p&gt;

&lt;p&gt;When someone clicks through from a Zap template to sign up, I wanted to know &lt;em&gt;&lt;em&gt;which&lt;/em&gt;&lt;/em&gt; template drove them. So the signup page accepts a &lt;code&gt;?ref=&lt;/code&gt; parameter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/signup?ref=zapier-stripe&lt;/code&gt; → "Chase failed Stripe payments automatically."&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/signup?ref=zapier-sheets&lt;/code&gt; → "Turn your spreadsheet into a payment chasing machine."&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/signup?ref=zapier-hubspot&lt;/code&gt; → "Close the deal. Chase the payment. Automatically."&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/signup?ref=zapier-forms&lt;/code&gt; → "From form submission to follow-up in seconds."&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/signup?ref=zapier&lt;/code&gt; → "Automate your payment follow-ups."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each ref swaps the hero headline and subtitle on the signup page. The person sees copy that matches their intent.&lt;/p&gt;

&lt;p&gt;The ref is also stored on the user record in the database as &lt;code&gt;signupRef&lt;/code&gt;. After 30 days of beinfg live I can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;signupRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;"User"&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;signupRef&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;signupRef&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That tells me which integration is driving signups, which use case resonates, and where to double down in listing copy. Without it, I would know signups are happening but not why.&lt;/p&gt;

&lt;p&gt;One column. Massive signal.&lt;/p&gt;

&lt;h2&gt;
  
  
  The partner review process
&lt;/h2&gt;

&lt;p&gt;Submitting to Zapier's partner program is straightforward but not instant. Here's how it went:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Build the integration&lt;/strong&gt; on Zapier's developer platform&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Submit for review.&lt;/strong&gt; Their team assigns a reviewer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compliance check.&lt;/strong&gt; The reviewer checked my homepage, terms of service, and privacy policy to confirm the country of operation. He couldn't find it. Fair, it wasn't there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix and respond.&lt;/strong&gt; I added "South Africa" to my Terms of Service, Privacy Policy, and Contact page within the hour. Updated the Governing Law clause to reference South African law.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wait for Beta.&lt;/strong&gt; After passing review, the integration moves to Beta in the Zapier app directory.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The compliance ask caught me off guard but makes sense. They're publishing your app in their directory, they need to know where you operate.&lt;/p&gt;

&lt;p&gt;If you're building a Zapier integration, put your country and legal jurisdiction on your site before you submit. Save yourself a round trip.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would do differently
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Start with the API earlier.&lt;/strong&gt; The &lt;code&gt;/api/v1/&lt;/code&gt; layer is clean and useful beyond Zapier. It could serve a mobile app, a CLI, or any other client. I should have built it as a first-class citizen from day one instead of bolting it on for Zapier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't overthink trigger architecture.&lt;/strong&gt; I spent time debating polling vs webhooks. Polling is fine. Ship it. You can add webhooks later if scale demands it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add signup attribution from the start.&lt;/strong&gt; The &lt;code&gt;signupRef&lt;/code&gt; field took 10 minutes to add. It should have been there from the first signup form.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Put your legal jurisdiction on your site from day one.&lt;/strong&gt; Not just for Zapier, for any partnership, integration, or compliance review. It took me an hour to fix. It should have been there from the start.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current status
&lt;/h2&gt;

&lt;p&gt;PayChasers has paying users across multiple countries. We are early, but the signal is real.&lt;/p&gt;

&lt;p&gt;The integration has been submitted and is in review. The embed page is built and waiting. The attribution tracking is live. When the green light comes, it's one env var and a deploy.&lt;/p&gt;

&lt;p&gt;If you're an indie hacker thinking about integrations, Zapier is high leverage. One integration surface, thousands of connections. Build the API, submit the integration, track your refs, and let the platform do the distribution.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I am Simangaliso, building PayChasers from South Africa. If you've ever sent a "just following up" email manually, PayChasers was built for you. &lt;a href="https://paychasers.com" rel="noopener noreferrer"&gt;paychasers.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>indiehacker</category>
      <category>saas</category>
      <category>zapier</category>
      <category>webdev</category>
    </item>
    <item>
      <title>A Terminal-Inspired Portfolio of Shipped and Researched Products (2026)</title>
      <dc:creator>Simangaliso Vilakazi</dc:creator>
      <pubDate>Fri, 02 Jan 2026 11:47:38 +0000</pubDate>
      <link>https://dev.to/smngvlkz/a-calm-terminal-inspired-portfolio-focused-on-shipped-products-ga8</link>
      <guid>https://dev.to/smngvlkz/a-calm-terminal-inspired-portfolio-focused-on-shipped-products-ga8</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/new-year-new-you-google-ai-2025-12-31"&gt;New Year, New You Portfolio Challenge Presented by Google AI&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  About Me
&lt;/h2&gt;

&lt;p&gt;I am a full-stack software engineer based in South Africa, focused on building durable software that ships and lasts.&lt;br&gt;
I care about clear system boundaries, low-maintenance architectures, and products that solve real problems without unnecessary complexity. Most of my work lives in production: SaaS tools, automation systems, payment flows, and community platforms.&lt;br&gt;
This portfolio is meant to reflect how I actually work. Pragmatic, intentional, and biased toward execution.&lt;/p&gt;
&lt;h2&gt;
  
  
  Portfolio
&lt;/h2&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__cloud-run"&gt;
  &lt;iframe height="600px" src="https://portfolio-1096924134982.us-central1.run.app"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;




&lt;p&gt;You can also visit it at: &lt;a href="https://smngvlkz.com" rel="noopener noreferrer"&gt;https://smngvlkz.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This portfolio is structured like internal system documentation rather than a traditional marketing site. It highlights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Live and archived products I have shipped&lt;/li&gt;
&lt;li&gt;Clear product intent, status, and scope&lt;/li&gt;
&lt;li&gt;Ongoing research and exploratory work&lt;/li&gt;
&lt;li&gt;SaaS, automation, payments, and platform work&lt;/li&gt;
&lt;li&gt;Open-source and community contributions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  AI Integration
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Design principle:&lt;/strong&gt;&lt;br&gt;
AI is used only where it improves inspectability or reduces cognitive load. All core functionality remains deterministic and debuggable without AI.&lt;/p&gt;

&lt;p&gt;This portfolio includes an interactive terminal (&lt;strong&gt;SYSTEM.QUERY&lt;/strong&gt;) powered by &lt;strong&gt;Google Gemini 3 Flash.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Users can query the &lt;strong&gt;entire&lt;/strong&gt; portfolio data directly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structured commands (handled locally for speed):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;help&lt;/strong&gt; - list available commands&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;list all&lt;/strong&gt; - show all products and contributions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;show activity&lt;/strong&gt; - show GitHub &amp;amp; GitLab stats, streak, sessions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;show [id] [field]&lt;/strong&gt; - show specific data for any item in the portfolio&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;explain [id]&lt;/strong&gt; - show full breakdown for any product or contribution&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;list fields&lt;/strong&gt; - show queryable fields&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Natural language queries (powered by Gemini) - examples:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"what tech does paychasers use?"&lt;/li&gt;
&lt;li&gt;"compare the infrastructure of all products."&lt;/li&gt;
&lt;li&gt;"which product uses blockchain?"&lt;/li&gt;
&lt;li&gt;"what's the Cape Community Blog built with?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The system prompt constrains Gemini to portfolio data only, with terminal-style output formatting. Common queries are handled locally without an API call. Complex or natural language queries fall back to Gemini.&lt;/p&gt;

&lt;p&gt;This demonstrates practical AI integration: fast where possible, intelligent when needed, without over-engineering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Live Activity Data (GitHub &amp;amp; GitLab)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;SYSTEM.ACTIVITY&lt;/strong&gt; pulls live data from GitHub and GitLab using authenticated API requests.&lt;/p&gt;

&lt;p&gt;A server-side route aggregates commits, repositories, contribution streaks, and session metadata into a unified activity model. This model is used in two places:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Rendered as live system activity stats in the interface, including a 111-day contribution heatmap that visualizes real contributions with interactive tooltips (showing source breakdown: GitHub/GitLab commits, date, and day of week). Only days with actual contributions are inspectable on hover.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Exposed to &lt;strong&gt;SYSTEM.QUERY&lt;/strong&gt;, allowing users to inspect and query the same data via the terminal.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This keeps activity up to date without manual updates and demonstrates real external API integration alongside AI-powered querying, with a single source of truth shared across UI and terminal&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Framework:&lt;/strong&gt; Next.js&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language:&lt;/strong&gt; Typescript, JavaScript&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Styling:&lt;/strong&gt; Tailwind CSS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI:&lt;/strong&gt; Google Gemini 3 Flash&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment:&lt;/strong&gt; Google Cloud Run (fully managed, container-based)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I used AI-assisted development to iterate on structure, copy clarity, and information hierarchy, while keeping the final implementation intentionally simple and deterministic.&lt;/p&gt;

&lt;p&gt;The site favors static rendering, fast load times, and a small surface area for long-term maintainability. The terminal feature adds interactivity without compromising the minimal aesthetic.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm Most Proud Of
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The restraint in the design. Nothing exists without a reason&lt;/li&gt;
&lt;li&gt;Clear labeling of product status (live, inactive, archived)&lt;/li&gt;
&lt;li&gt;Showing real shipped work instead of demo projects&lt;/li&gt;
&lt;li&gt;AI integration that fits the product's identity (terminal queries, not chatbot fluff)&lt;/li&gt;
&lt;li&gt;A portfolio that reflects engineering maturity, not trend-chasing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not a highlight reel. It is a snapshot of how I build software today.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>googleaichallenge</category>
      <category>portfolio</category>
      <category>gemini</category>
    </item>
  </channel>
</rss>
