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
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.
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.
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 — 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
Multi-tenant isolation belongs in the database (RLS), not the application — application bugs cannot cause data leaks
- 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
Atomic Redis SET NX is the correct deduplication pattern — GET+SET has a race condition; 360dialog can deliver the same message multiple times
- 4
Fail-open on Redis unavailability: log and allow the request — production cannot go down because a cache layer is unhealthy
- 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
Debt recovery tone must be in the tenant's own voice — Claude drafts per-tenant, not generic templates
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
# 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
More Projects
Interested in similar work?
Let's discuss how I can build something like this for your business.
Let's Talk