InboxOps — Recommended Rough Implementation Order
Purpose: This document recommends a high-level implementation order for InboxOps. It is the base for a future detailed implementation plan with milestones and subtasks. It does not contain subtasks or timelines — only the recommended sequence of work so that dependencies are respected and the product becomes testable and shippable in stages.
Source of truth: PRODUCT-AND-ARCHITECTURE.md (features, data model, architecture, UX).
Detailed implementation plan: implementation-plan/INDEX — one milestone file per phase with detailed tasks and acceptance criteria.
Principles
- Foundation first: Repo, database, auth, and tenant context must be in place before any feature that depends on them.
- Vertical slices where possible: Prefer getting one flow end-to-end (e.g. “connect inbox → see ticket from email → reply”) over building all backends then all UIs.
- Dependencies respected: Email ingestion needs inboxes, encryption, worker, and object storage; ticket list needs tickets (from ingestion or manual create); billing needs subscription model and Stripe; etc.
- Defer add-ons: SSO, Gmail Push, Graph change notifications, and advanced reporting refinements can follow the core launch path.
Recommended Order
1. Foundation and repository
- Monorepo: Turborepo, pnpm workspaces,
apps/web(Next.js App Router),apps/worker(Inngest),packages/(db, auth, api, config, ui, email, billing, notifications). - Database: Drizzle schema for all entities (Section 5), migrations, Neon connection (pooled + unpooled), indexes (Section 5.22), RLS policies,
updated_attriggers. - Config: Env validation (e.g. Zod in
packages/config),.env.examplefrom Section 9. - Next.js skeleton: Route groups
(marketing),(auth),app/[slug],api/; no feature logic yet. - i18n: next-intl in apps/web from day one (Section 3.13);
messages/en/with namespace files (common, auth, tickets, settings, etc.); default localeen; no locale switcher in v1; all user-visible strings via useTranslations/getTranslations.
Exit state: Repo runs; DB migrates; app and worker start; no auth or tenant logic yet.
2. Auth and tenant identity
- Auth: Lucia setup;
users,sessions; sign up (email + password, 12 char min), sign in, sign out; password reset (request + token page); bcrypt, HTTP-only session cookie. - Tenant model: Signup creates
tenant+tenant_membership(admin); slug,onboarding_completed_at,trial_ends_at,ticket_number_seq. - Middleware: For
/app/[slug]/** — resolve tenant by slug → validate session → check membership → redirect to/no-workspaceif no tenants → onboarding redirect if not completed → trial/lock redirect; set tenant/user/role in context. - Public auth pages:
/login,/signup,/reset-password,/reset-password/[token]; post-login redirect to/app/[slug]/tickets(last-used or first tenant). /no-workspacepage for authenticated users with no tenant memberships.
Exit state: User can sign up, sign in, and be redirected into the app under a tenant slug; middleware enforces auth and tenant.
3. App shell and navigation
- App layout for
/app/[slug]/**: sidebar (logo, nav, inbox list placeholder, user menu), top bar (title, search placeholder, notification bell placeholder). useTenantPathhelper; all in-app links use it (no hardcoded slugs).- Tenant (workspace) switcher for users with multiple memberships; persist last-used tenant (e.g. cookie/session).
- Role-based nav: Settings only for Admin; Viewer restrictions.
- Theming: next-themes, dark/light/system; persist preference; no FOUC.
- Shared UI: empty states, loading skeletons, error boundaries; responsive shell (collapse sidebar, drawer on mobile).
Exit state: Authenticated user sees a consistent app shell and can switch tenants; no real ticket or inbox data yet.
4. Onboarding and tenant settings
- Onboarding wizard: steps (org confirm, connect first inbox, invite team optional, done); persist step; set
onboarding_completed_aton completion; redirect to/app/[slug]/tickets. - Settings → General: org name, slug, timezone; danger zone (delete org); slug change warning.
- Feature flags: DB-backed
feature_flagsand optionalfeature_flag_overrides; config loader for app and worker.
Exit state: New tenant completes onboarding; admin can edit org settings; feature flags available for gating.
5. Inbox management (without live email sync)
- Inbox CRUD: list, create (name + connection type), update, archive; enforce plan inbox limit (from subscription/trial).
- Connect-inbox UI: choose type (IMAP, Gmail, Microsoft); IMAP form (host, port, credentials) + test connection; Gmail/Microsoft OAuth flows (real or stub that stores placeholder); store credentials encrypted (AES-256, key from env).
- Inbox connection status:
last_error,connected_at; show in list and settings; “Disconnected” state and reconnect CTA. - Inbox settings page: edit name, connection (re-auth for OAuth), auto-response toggle/body (optional), aliases (optional; can defer).
- Encryption: credential and OAuth token encryption in
packages/emailor shared; key rotation documented (Section 7.1).
Exit state: Admin can create inboxes, connect with IMAP or OAuth, and see connection status; no background fetch or ticket creation yet.
6. Email pipeline and worker
packages/email: IMAP (imapflow), Gmail API, Microsoft Graph clients; MIME parsing (mailparser); send via SMTP (nodemailer) and Gmail/Graph; credential/token loading and encryption.- Object storage: S3 client; tenant-prefixed keys; upload (inbound/outbound attachments), pre-signed download URLs; size limits (Section 6.7).
- Worker: Inngest app; “fetch new messages” per connection type (polling: IMAP 5 min, Gmail/Graph 5 min fallback); “send reply” job; concurrency per inbox and per send; rate limits.
- Inbound pipeline: fetch → parse → spam/bounce/OOO checks (Section 2.3) → dedupe by
(inbox_id, message_id_header)→ thread match or create ticket → insert message(s), attachments to S3 → optional auto-response job → optional webhook/notification fan-out. - Outbound pipeline: tRPC creates message row
send_status: 'pending'→ worker builds MIME (In-Reply-To, References), sends via inbox connection → updatesend_statusandsent_at; on failure setsend_errorand optionally notify. - Bounce handling: set
messages.bounce_received_aton linked outbound message (Section 2.3, 5.6). - Gmail watch and Graph subscription (optional in this phase): can be added after polling works; Section 8.3, 8.4.
Exit state: Inbound email creates/updates tickets and messages; agent reply from UI is sent as email; attachments stored in S3; edge cases handled.
7. Ticket list and detail (core UX)
- Ticket list: tRPC list with filters (status, assignee, inbox, tags, date range), sort, pagination; ticket number, subject, inbox, status, assignee, updated_at.
- Ticket detail: load ticket + messages by
ticket_id; thread view (chronological); reply composer (Tiptap, HTML); internal note toggle; status and assignee inline edit. - Reply from UI: submit → insert outbound message (pending) → Inngest send job → optimistic update or refetch; show send failure and retry.
- Tags: CRUD tags (tenant-scoped); ticket_tags junction; add/remove on ticket; filter list by tag.
- Assignee: assign/reassign; enforce tenant membership; notifications (can stub or full in step 8).
- Real-time: React Query
refetchInterval(thread 5s, list 15s);refetchOnWindowFocus.
Exit state: Agent sees ticket list, opens ticket, sees thread, replies and adds internal notes; replies are sent via worker; tags and assignee work.
8. Notifications and snippets
- Notifications table and tRPC: create notification (worker or tRPC), list by user/tenant, mark read; unread count.
- Notification events: ticket assigned, reply received, @mention (internal note), SLA breach/near-breach, inbox error, trial/billing (Section 2.10); fan-out from worker or tRPC.
- Transactional email: react-email templates (Section 2.10b), Postmark delivery; invite, password reset, ticket assigned, trial reminders, payment failed, etc.
- Notification preferences: per user/tenant/event (email, in-app, both); Settings or Profile.
- In-app notification panel: bell icon, list, mark all read, link to ticket/page.
- Snippets: CRUD (tenant-scoped); snippet picker in reply composer; insert and edit before send.
Exit state: Agents get in-app and email notifications for assignments and replies; snippets available in composer; all system emails sent via Postmark.
9. Invites and team
- Invitations: create invite (token, expiry), send email (Postmark); accept flow: existing user → add membership → redirect; new user → set password page → create user + membership → redirect.
- Settings → Team: list members (role, remove), list pending invites (resend, revoke); invite member (email + role).
- Audit log: write on key actions (Section 2.9); Settings → Audit: table, filters (date, actor, action); retention by plan.
Exit state: Admin can invite users; invitee accepts and joins tenant; audit log visible to Admin.
10. Billing and trial
- Subscription and trial:
subscriptionsrow per tenant;trial_ends_aton tenant; plan_id, inbox_limit, status; entitlement sync (Section 2.6). - Stripe: createCheckoutSession (Starter/Growth/Business price IDs), createPortalSession; webhook handler for subscription and invoice events; idempotent entitlement sync; Enterprise = manual, no price ID (Section 2.6, 9.7).
- Trial lifecycle: cron (e.g. daily) — day-25 reminder email, day-30 expiry handling (banner, block inbox create), day-37 lock; grace period 7 days then locked.
- Locked state:
/app/[slug]/lockedpage; middleware redirect when locked; read-only tickets, no reply, no sync. - Billing UI: Settings → Billing — plan card, trial banner, “Manage billing” (Portal), upgrade CTA; enforce inbox limit on create.
Exit state: Trial and paid plans work; Stripe Checkout and Portal; tenant can be locked after trial; inbox limit enforced.
11. SLA and business hours
- SLA policies: CRUD (name, first_response_hours, resolution_hours, business_hours_only, inbox or global); assign to inbox or “all”.
- Business hours:
business_hours(timezone, per-day start/end); default when missing (Section 5.21); Settings → SLA. - SLA status on ticket: compute from policy +
first_response_at/resolved_at; display in ticket sidebar (on track / due soon / breached); pause when status = Waiting. - SLA cron: periodic check for near-breach and breach; enqueue notification jobs (Section 4.8).
Exit state: Admin configures SLA and business hours; tickets show SLA status; breach/near-breach trigger notifications.
12. Search
- Full-text search: PostgreSQL
tsvectorontickets.subjectandmessages.body_text(Section 2.11); GIN indexes; generated columns (raw SQL in migration). - Search API: tRPC search with
websearch_to_tsquery, tenant_id; optional filters (sender, tag) as WHERE, not FTS. - Ticket list: search input (debounced), combine with existing filters; results in same list UX.
Exit state: Agents can search tickets and messages by subject/body; sender and tag as filters.
13. Reporting
- Basic reporting: ticket volume over time, status distribution, first response time, resolution time, by inbox/assignee; date range; tRPC or server queries; cache briefly if needed.
- Reports page: layout, date range picker, stat cards, bar/line charts (Recharts).
- Advanced reporting (Growth+): SLA compliance, tag distribution, agent workload, CSV export; gated by
advanced_reportsentitlement.
Exit state: Agents and admins see basic (and if entitled, advanced) reports.
14. Integrations (webhooks and public API)
- Webhooks: WebhookEndpoint CRUD (URL, secret, events); on ticket/message/inbox events, enqueue Inngest job; POST to URL with payload and HMAC; retries (Section 2.8, 4.9); WebhookDelivery log; Settings → Webhooks (Business+).
- Public API: REST
/api/v1/; API key auth (hash, prefix in UI); tenant from key; rate limit (DB sliding window); endpoints for tickets, messages, inboxes, tags (Section 2.8); OpenAPI spec and docs; gated bypublic_apientitlement; no outbound reply via API.
Exit state: Tenants with entitlement can configure webhooks and use the public API for read and limited write.
15. Marketing site
- Marketing route group: landing page (hero, value prop, features, CTA); pricing (plan table, trial CTA, Enterprise → contact); contact form (ContactSubmission table, Postmark to CONTACT_EMAIL, honeypot, rate limit).
- Legal: MDX pages for terms, privacy, impressum (DE); LegalLayout; placeholder or lawyer-reviewed copy.
- SEO: metadata, sitemap, robots.txt; Plausible (optional env); no cookie banner for strict necessity only.
- Blog: stub route “Coming soon” if required.
Exit state: Marketing and legal pages live; contact form stores submissions and sends email.
16. Polish, error states, and observability
- Error and system pages: 404, 500, tenant not found, locked, maintenance (Section 6.8); inbox disconnected banner in ticket view; send-failed state and retry in thread.
- Command palette: cmdk, Cmd+K; navigation, ticket actions, theme, sign out (Section 6.9).
- Profile: name, change password, notification preferences, theme (Appearance); optional avatar.
- Accessibility and responsive: keyboard nav, ARIA, contrast; breakpoints for list/detail and sidebar.
- CI/CD: GitHub Actions — build, lint, test (Vitest), E2E (Playwright); deploy web and worker to Fly.io (staging from
staging, prod frommain/tags). - Observability: Sentry (web + worker), source maps; Logtail; Better Stack Uptime on
/api/health; no PII in Sentry (Section 3.15). - Security and ops: RLS verification; rate limits in place; encryption key rotation and backup/restore documented (Section 7).
Exit state: App is production-ready; errors and locked states handled; monitoring and deploy automated.
17. Add-ons and post–first launch (optional in rough order)
- SSO (SAML/OIDC): Enterprise entitlement; IdP config in Settings → SSO; sign-in with SSO; link user by email/subject.
- Gmail Push: Pub/Sub topic and subscription; watch per inbox;
gmail_watch_expires_at; renewal cron (Section 8.3). - Microsoft Graph change notifications: Subscription per inbox; validation handshake; renewal cron (Section 8.4).
- Advanced reporting refinements: Additional metrics, exports, or visualizations as needed.
18. Customer-facing documentation (inboxops-docs)
- Repository: inboxops-docs (in this workspace). URL: docs.inboxops.app. Tech: Docusaurus. Content: Markdown.
- Scope: Public docs for customers and integrators: intro, getting started, guides (inboxes, tickets, team, billing, API/webhooks), API reference, optional changelog/FAQ. Content in Markdown; Docusaurus for build and nav.
- Deploy: Site buildable and deployable to docs.inboxops.app; CI optional. Docs kept in sync with product (owner or process).
- Detail: See implementation-plan/M18-documentation.
Summary table
| Phase | Focus | Key deliverables |
|---|---|---|
| 1 | Foundation | Repo, DB schema, migrations, config, app skeleton |
| 2 | Auth & tenant | Sign up/in, password reset, tenant from slug, middleware, /no-workspace |
| 3 | App shell | Layout, nav, tenant switcher, theme, empty/loading states |
| 4 | Onboarding & settings | Wizard, General settings, feature flags |
| 5 | Inboxes | CRUD, connect (IMAP/OAuth), encryption, status UI |
| 6 | Email pipeline | Worker, fetch/send, S3, dedupe, thread match, edge cases |
| 7 | Tickets | List, detail, thread, reply, tags, assignee, polling |
| 8 | Notifications & snippets | In-app + email, preferences, snippets in composer |
| 9 | Team & audit | Invites, team settings, audit log |
| 10 | Billing & trial | Stripe Checkout/Portal, webhooks, trial lifecycle, locked |
| 11 | SLA | Policies, business hours, status, breach cron |
| 12 | Search | FTS, filters, list integration |
| 13 | Reporting | Basic + advanced, Reports page |
| 14 | Integrations | Webhooks, Public API |
| 15 | Marketing | Landing, pricing, contact, legal |
| 16 | Polish | Error pages, command palette, profile, a11y, CI/CD, observability |
| 17 | Add-ons | SSO, Gmail Push, Graph notifications (as needed) |
| 18 | Customer documentation | Docusaurus in inboxops-docs; docs.inboxops.app; Markdown content |
Notes for detailed planning
- Each phase above can be split into milestones (e.g. “2a: Auth only”, “2b: Tenant + middleware”).
- Subtasks should reference PRODUCT-AND-ARCHITECTURE.md sections (e.g. “5.6 Message”, “4.10 Deduplication”) so acceptance criteria are traceable.
- Parallelization: Within a phase, some work can run in parallel (e.g. worker + S3 + email package in phase 6; webhook delivery + Public API in phase 14).
- Testing: Unit/integration (Vitest) and E2E (Playwright) should be planned per phase; auth and tenant resolution are critical paths for E2E early.
- Staging: Deploy staging (Fly + Neon branch) as soon as phase 2 or 3 is stable so every subsequent phase can be verified in a live environment.