Skip to main content

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.

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_at triggers.
  • Config: Env validation (e.g. Zod in packages/config), .env.example from 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 locale en; 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-workspace if 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-workspace page 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).
  • useTenantPath helper; 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_at on completion; redirect to /app/[slug]/tickets.
  • Settings → General: org name, slug, timezone; danger zone (delete org); slug change warning.
  • Feature flags: DB-backed feature_flags and optional feature_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/email or 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 → update send_status and sent_at; on failure set send_error and optionally notify.
  • Bounce handling: set messages.bounce_received_at on 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: subscriptions row per tenant; trial_ends_at on 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]/locked page; 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.


  • Full-text search: PostgreSQL tsvector on tickets.subject and messages.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_reports entitlement.

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 by public_api entitlement; 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 from main/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

PhaseFocusKey deliverables
1FoundationRepo, DB schema, migrations, config, app skeleton
2Auth & tenantSign up/in, password reset, tenant from slug, middleware, /no-workspace
3App shellLayout, nav, tenant switcher, theme, empty/loading states
4Onboarding & settingsWizard, General settings, feature flags
5InboxesCRUD, connect (IMAP/OAuth), encryption, status UI
6Email pipelineWorker, fetch/send, S3, dedupe, thread match, edge cases
7TicketsList, detail, thread, reply, tags, assignee, polling
8Notifications & snippetsIn-app + email, preferences, snippets in composer
9Team & auditInvites, team settings, audit log
10Billing & trialStripe Checkout/Portal, webhooks, trial lifecycle, locked
11SLAPolicies, business hours, status, breach cron
12SearchFTS, filters, list integration
13ReportingBasic + advanced, Reports page
14IntegrationsWebhooks, Public API
15MarketingLanding, pricing, contact, legal
16PolishError pages, command palette, profile, a11y, CI/CD, observability
17Add-onsSSO, Gmail Push, Graph notifications (as needed)
18Customer documentationDocusaurus 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.