All Projects
Beta2025AI/ML · SaaS · SME Tools

AdminOS

The AI Operating System for South African SMEs

Full-stack AI business OS replacing 6 separate SaaS subscriptions. Five specialist Claude agents handle communication, invoicing, HR, reporting, and customer service — all accessible via WhatsApp. Built for South African load-shedding, PayFast, and isiXhosa context.

Serving

2M+ SA SMEs underserved by global SaaS

Market

R11,200/mo replaced by R2,500/mo

Problem

Tool fragmentation + WhatsApp dependency

Result

5 agents replace 6 subscriptions

The Problem

What needed solving

SA SMEs pay R11,200/month across fragmented tools — CRM, invoicing, HR, comms, analytics — most of which they barely use and none of which talk to each other.

The Solution

How I built it

5 specialist AI agents (Sales, Finance, HR, Comms, Analytics) replacing 6 subscriptions. WhatsApp-native interface — no app switching required. Xero-integrated, PayFast-enabled, load-shedding-aware offline mode.

Build Journey

Africa's businesses run on WhatsApp. Millions of messages land every day — client queries, invoice follow-ups, leave requests, complaints — and behind each one is a human manually responding, copying, chasing, and repeating. AdminOS was built to fix that. Not as a chatbot. As an operating system — one that handles the full admin layer automatically. Multi-tenancy is enforced at the database layer, not the application layer. Every table has tenant_id as a required column. Supabase RLS policies verify tenant_id = auth.jwt() ->> 'tenant_id' — a bug in the application code cannot leak one business's data to another. The middleware injects x-tenant-id, x-user-id, and x-user-role into every authenticated request header. The audit log is append-only — no UPDATE or DELETE policy granted. The most important cost decision: Claude prompt caching on the tenant system prompt. Every tenant has a pre-built context string (business name, type, language, tone, FAQs, staff directory, services, policies, extracted company goals) marked cache_control: ephemeral. Subsequent calls that hit the cache cost 90% less per token. Result: 85% reduction in AI operating costs at scale. The WorkflowEngine runs 7 steps in sequence with per-step timeouts: deduplication (Redis SET NX atomic, 500ms), tenant context load (2s), FAQ cache check (2s), Claude response with caching (20s), 360dialog outbound (5s), audit log write (2s), Supabase Realtime dashboard push (1s). The 360dialog webhook must receive a 200 OK in under 1 second — the pipeline runs async, non-blocking, after the webhook responds. Fail-open on Redis unavailability: log the error, allow the request. Production cannot go down because a cache layer is unhealthy. The debt recovery engine runs a 5-tier escalation sequence over 30 days via Vercel Cron at 09:00 SAST daily. Claude drafts each message in the tenant's own voice and tone. The wellness check-in sends daily WhatsApp mood check-ins to all staff (Mon–Fri 08:00 SAST). Burnout detection triggers a manager alert when the 7-day average drops below 2.5.

AdminWorkflowEngine — async pipeline with dedup, caching, and multi-tenant RLS
typescript
// AdminWorkflowEngine — 7-step async pipeline with per-step timeouts
// 360dialog webhook must receive 200 OK in < 1s; pipeline runs non-blocking

export async function processWhatsAppMessage(
  message: InboundMessage,
  tenantId: string
): Promise<void> {
  // Respond to 360dialog immediately — pipeline is fire-and-forget from webhook's perspective
  void runPipeline(message, tenantId);
}

async function runPipeline(message: InboundMessage, tenantId: string) {
  // Step 1: Deduplication — atomic Redis SET NX (no GET+SET race condition)
  const deduped = await redis.set(`msg:${message.id}`, '1', { nx: true, ex: 86400 });
  if (!deduped) return; // Already processed — 360dialog can send the same message 2-3x

  // Step 2: Load tenant context (cached in Redis, 15min TTL)
  const context = await withTimeout(loadTenantContext(tenantId), 2000);

  // Step 3: FAQ cache check — answer without AI if possible (Redis, 7-day TTL)
  const cached = await withTimeout(checkFAQCache(message.text, tenantId), 2000);
  if (cached) return sendAndLog(cached, message, tenantId);

  // Step 4: Claude with prompt caching — 85% cost reduction on cache hits
  const response = await withTimeout(
    anthropic.messages.create({
      model: 'claude-sonnet-4-6',
      system: [{ type: 'text', text: context.systemPrompt, cache_control: { type: 'ephemeral' } }],
      messages: buildConversationHistory(message, context), // Capped at 10 msgs
    }),
    20000
  );

  // Steps 5–7: Send → Audit → Dashboard (always attempted, even after prior failures)
  await withTimeout(sendVia360dialog(response.content[0].text, message.from), 5000);
  await withTimeout(logToAudit(message, response, tenantId), 2000);
  await withTimeout(pushToSupabaseRealtime(tenantId, message), 1000);
}

// Multi-tenant RLS — enforced at DB layer, not application layer
// CREATE POLICY "Tenant isolation" ON conversations
//   USING (tenant_id = (auth.jwt() ->> 'tenant_id')::uuid);
// A bug in application code cannot leak Business A's data to Business B.

What This Taught Me

  1. 1

    Multi-tenant isolation belongs in the database (RLS), not the application — application bugs cannot cause data leaks

  2. 2

    Prompt caching on the tenant system prompt = 85% AI cost reduction; the cached block is pre-built business context, the dynamic block is per-message

  3. 3

    Atomic Redis SET NX is the correct deduplication pattern — GET+SET has a race condition; 360dialog can deliver the same message multiple times

  4. 4

    Fail-open on Redis unavailability: log and allow the request — production cannot go down because a cache layer is unhealthy

  5. 5

    Per-step timeouts prevent one slow step (Claude at 20s max) from blocking the audit log and dashboard steps that must still run

  6. 6

    Debt recovery tone must be in the tenant's own voice — Claude drafts per-tenant, not generic templates

Tech Stack & Decision Rationale
01

Next.js 14

App Router + API routes for agent orchestration

02

TypeScript

Type-safe agent message contracts

03

Supabase

Multi-tenant RLS — each SME sees only their data

04

Claude API

5 specialist agents with shared context cache

05

Upstash Redis

Rate limiting + prompt cache warm storage

06

Meta WhatsApp Cloud API

Primary client interface — SA businesses live on WhatsApp

07

PayFast

ZAR subscription billing

08

Resend

Automated invoice + report delivery

README.md — adminos
📄 README.md
# AdminOS
> The AI Operating System for South African SMEs

## Project Context
**Category:** AI/ML · SaaS · SME Tools
**Status:** Beta · 2025
**Author:** Nandawula Regine Kabali-Kagwa — East London, South Africa
**Company:** Mirembe Muse (Pty) Ltd · Reg: 2026-005658

## Stack
```
Next.js 14                     # App Router + API routes for agent orchestration
TypeScript                     # Type-safe agent message contracts
Supabase                       # Multi-tenant RLS — each SME sees only their data
Claude API                     # 5 specialist agents with shared context cache
Upstash Redis                  # Rate limiting + prompt cache warm storage
Meta WhatsApp Cloud API        # Primary client interface — SA businesses live on WhatsApp
PayFast                        # ZAR subscription billing
Resend                         # Automated invoice + report delivery
```

## Architecture Notes
- All data mutations validated server-side via Next.js API routes
- Row-Level Security enforced at database level (Supabase)
- Mobile-first, PWA-ready, offline-tolerant where connectivity is unreliable
- PayFast integration for ZAR-native payments (no USD conversion)
- SEO-optimised: metadata, JSON-LD, canonical URLs, sitemap
- POPIA compliant — data minimisation + user consent by design

## Environment Variables
```env
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY=
ANTHROPIC_API_KEY=
NEXT_PUBLIC_PAYFAST_MERCHANT_ID=
NEXT_PUBLIC_PAYFAST_MERCHANT_KEY=
PAYFAST_PASSPHRASE=
RESEND_API_KEY=
```

## Links
- Live:   Coming soon — domain propagating
- GitHub: Private repository
- Portfolio: https://creativelynanda.co.za/projects/adminos

---
Built from East London, South Africa · Nine months · Zero to production

Interested in similar work?

Let's discuss how I can build something like this for your business.

Let's Talk