Inspiration

Operations teams juggle CRM, support tickets, tasks, email, and reporting across half a dozen tools. Every lead follow-up, ticket triage, and status update is manual overhead. OpsPilot started as an experiment: can a single AI assistant, scoped to one workspace, plan and execute all of that from natural language?

What it does

An AI assistant that turns natural language into safe, auditable operations. Type "send a warm welcome to jane@acme.com, create a lead, and add a follow-up task" — it plans the actions, validates them, executes CRM/task/email tools, and logs every step to an audit trail. Also includes event-driven workflow automation: when a lead or ticket is created, matching workflows run automatically. Routes: Landing → login → dashboard with assistant chat, CRM leads, tasks, support tickets, workflows, reports, settings.

How we built it

Next.js 16 + React 19 + TypeScript + Auth.js (custom JWT HMAC-SHA-256, credentials-only) + Prisma 7 (driver adapter, PostgreSQL) + Vercel AI SDK + HCNSEC OpenAI-compatible relay + Tailwind 4 + shadcn UI. nodemailer SMTP for outbound email. Business logic lives in lib/ops/*, not in routes. Each mutating function wraps in a db.$transaction and writes an ActivityLog row — the audit trail is part of the unit of work. The AI agent is a planner + safe tool executor: it produces typed action plans (6 types), validated by Zod, and never executes arbitrary code or deletes records. Structured JSON from the AI is extracted via regex {...} block extraction with one retry — no provider-native JSON mode.

Challenges we ran into

  • Next.js 16 has breaking changes — proxy.ts instead of middleware.ts, deprecation notices everywhere. Every new page required checking the installed version's docs.
  • Prisma 7 driver adapters — client is at generated/prisma not @prisma/client, PrismaPg adapter pattern, and the AutomationRunStep model isn't always visible to a stale dev client (defensive runtime cast required).
  • AI structured output without native JSON mode — the extractJsonObject + retry approach works but adds latency; the model sometimes wraps JSON in markdown fences or adds trailing prose.
  • Multi-tenancy without RLS — every single query must include where: { workspaceId }. Miss one and it's a cross-tenant leak. No room for error.
  • Event-driven workflow loops — a workflow creating a child record could trigger itself. Solved with a suppressEvents flag on automation-originated mutations. Accomplishments that we're proud of
  • One assistant command can create a CRM lead, support ticket, follow-up task, generate a customer email, log to audit, and fire a workflow — all from natural language.
  • The fallback planner (deterministic typo-tolerant parser) works when the AI provider is unavailable, so the demo never completely breaks.
  • Audit trail is universal — every mutation (human or AI) writes an ActivityLog inside the same transaction. Nothing happens invisibly.

What we learned

  • Business logic in a separate layer (lib/ops/*) is worth the upfront structure — routes stay thin and the ops functions are independently testable.
  • Prisma 7 driver adapters are the future but the migration path is rough (different import path, adapter instantiation, stale client edge cases).
  • An AI agent constrained to typed actions is far safer and more predictable than letting it write code, but the natural-language-to-plan mapping is the hardest part to get right.

What's next

Production-grade inbound email via webhook providers (Resend, SendGrid, Mailgun) instead of IMAP polling. External CRM/Slack integrations through the existing adapter stubs. Approval queues for high-risk AI actions. Multi-workspace admin UIs.

Built With

  • next
  • openai
Share this project:

Updates