All projects

Case study · 4 min read

OMO AI

An AI marketing assistant for small businesses, built with a team.

OMO AI landing page with the headline "Real insights. Better content. Smarter ads. More sales." and a chat box
My role
Contract engineer on a small team: authentication and onboarding, settings, billing and pricing UI, the role-based admin console, the landing page and analytics and integrations screens, and I worked on the AI agent (LangChain and LangGraph) with the lead.
Timeline
Jul 2025 - Dec 2025
Stack
Next.js, TypeScript, NextAuth, Prisma, PostgreSQL, Stripe, LangChain, LangGraph, GSAP, Docker, Turborepo

OMO AI analyses a business's content, ads and audience, then tells its owner what to post, when to post and how to grow. It is a team product. I joined as a contract engineer and worked on accounts and onboarding, settings, billing screens, the role-based admin console, the landing page and analytics screens, and I worked on the AI agent (LangChain and LangGraph) with the project lead.

The problem

Small business owners rarely have time to work out what to post, whether their ads work, or who their audience is. OMO AI puts an assistant in front of that data: ask a question in plain language and get analysis and next steps.

A product like that also needs the unglamorous parts to be solid: accounts, billing, settings, and an internal console for the team running it.

Constraints

  • A shared codebase. Three engineers on one monorepo, with a shared database schema that changes often.
  • Security. An admin console that can suspend users and change roles has to be locked down properly.
  • Moving requirements across a long build, with several rounds of rebasing to keep branches in step.
  • Confidentiality. This is a company product, so this page stays at a level that is safe to publish.

Approach

The product is a Turborepo and pnpm monorepo with a Next.js web app and a shared UI package, using server actions, NextAuth for sign-in, Prisma on PostgreSQL, Stripe subscriptions and a docs site. The assistant is a LangGraph agent with tools, memory kept in Postgres and a streaming chat.

I worked on the AI agent alongside the lead, and I built the account, settings, billing, admin and landing areas described below.

Architecture at a glance

A high-level view only; internal details are intentionally left out.

In the app

The landing page on a phone.

Key decisions

01Permissions live in the database and are enforced on the server

Context
An admin console where roles are hard-coded, or only hidden in the UI, is one edited request away from a breach.
What I chose
Roles are stored with a permissions object per module. Every admin server action loads the caller's role and checks the permission before doing anything, a super admin gets all permissions, and users without access land on a no-access page. The console also refuses to load on a phone.
What I passed on
Hard-coded roles in the code, or hiding buttons in the interface only.
Trade-off
A database lookup on each action, in return for roles that admins can change without a deploy.

02Sign-in that handles the awkward cases

Context
Accounts are the front door: sign-up, verification, recovery and third-party sign-in all need to be right.
What I chose
Email and phone verification with one-time codes, Google sign-in (added in September 2025), forgot-password and reset flows, and a separate admin sign-in and invited-admin sign-in with their own one-time codes. A location lookup during sign-up fills in country details.
What I passed on
Password-only accounts.
Trade-off
More flows to build and test, many of which most users never see.

03Settings as a module, not a page

Context
Profile, notifications, security and billing grow independently.
What I chose
Settings is split into general, profile, notifications, security and billing sections, each with its own route and actions, with theme preference synced to the account. Billing has a pricing page and a subscription model behind it.
What I passed on
One long settings page.
Trade-off
More routes, but each one stays small and testable.

04A person can approve what the agent wants to do

Context
An assistant connected to business accounts should not act on its own where a mistake would matter.
What I chose
The agent has approve and deny endpoints, so a tool run can wait for a person, and its memory and checkpoints are kept in Postgres so a conversation can resume. I worked on this with the lead.
What I passed on
Let the agent run every tool autonomously.
Trade-off
More friction for the user in exchange for control over actions that matter.
How an admin action is authorised

The check happens on the server for every action.

Challenges

  • Keeping branches in step. A busy three-person repo meant frequent rebases and merge conflicts around a shared Prisma schema.
  • Getting permissions right. The admin console went through several passes: fixing role access handling, an access modal and suspended-user flows.
  • Scope. Landing page, analytics, integrations and admin work were all moving at the same time.

Results

OMO AI is live. My part shipped the account and onboarding flows, the settings module, billing and pricing screens, the role-based admin console with roles and team management, the animated landing page, and the analytics and integrations screens.

As a company product there are no business metrics I can share, so I will not quote any.

What I would do next

  • Add automated tests around the permission checks, which are the highest-risk code in the console.
  • Keep an audit trail of admin actions such as suspensions and role changes.

Contact

Exploring a new product idea? Looking for ways to improve your product? Send a mail to: