Modular IT Ticketing Platform
Portal, email and chat requests in a single queue, with AI triage that must prove itself
- Platform
- AI
- PWA
- Client
- VNE Tech Solutions
- Sector
- IT service management
- Year
- 2026
- Platforms
- Web · Installable PWA
- Stage
- MVP

The project at a glance.
This IT service desk lets staff report a problem or request equipment and access through a simple portal, by email or from the chat tools they already use, while the IT team works every request from a single queue with clear response deadlines. AI suggests how each new ticket should be categorised and routed, but may only act on its own once it has proved accurate on that organisation's own tickets. The same installation can serve many organisations, each with its own data kept separate.
- Who it serves
- Staff and requestersReport a problem in their own words, request from a catalogue, follow progress in plain language and approve requests from a phone.
- Service desk agentsOne queue across every channel, SLA clocks, AI triage suggestions to accept or dismiss, replies kept apart from internal notes, and time logging.
- IT administratorsSet service levels, the priority matrix and business rules, publish catalogue items and workflows, and oversee AI triage from one console, with every change in the audit trail.
Support requests are scattered and hard to trust
IT requests arrive by email, chat, phone and web forms, and each channel tends to become its own inbox. Agents copy details between tools, requests slip through the gaps, and nobody can say with confidence whether a promised response time was met.
Organisations want AI to help sort incoming work, but a model that quietly re-routes tickets or changes priorities is hard to trust, hard to audit and costly when it is wrong.
A provider serving several organisations carries a further duty: each customer's records must stay separate, and every change must be traceable to the person or system that made it.
Many front doors, a single ticket record, governed automation
VNE designed and built a modular IT service-management platform around a single ticket record. The self-service portal, email, Slack, Microsoft Teams, WhatsApp and records of completed phone calls all create and update the same ticket, and three web applications sit on top: a requester portal, an agent workbench and an admin console.
Service levels, approval policies, business rules, workflows, request forms and the catalogue are held as configuration rather than code. Rules, workflows and forms are published as numbered versions, and rules and workflows can be rolled back. AI triage moves through explicit modes (off, shadow, suggest, auto) and every decision it makes is recorded with its provider, confidence and cost.
Underneath is a TypeScript modular monolith of 27 domain modules on PostgreSQL, with row-level security on every tenant table, a transactional outbox for reliable events and a hash-chained audit trail.
What it does.
Every channel, one ticket
The portal, email, Slack, Teams, WhatsApp and completed-call records all raise and update the same ticket. Each inbound webhook is verified before it is trusted, and loop protection stops a channel replying to itself.
Self-service portal and catalogue
Requesters describe a problem in their own words or pick from a catalogue whose forms show and require fields only when they apply. Statuses are written for the requester, and priorities are never shown to them.
Agent workbench
A live queue and a three-pane ticket workspace with the full timeline, replies kept apart from internal notes, status changes, assignment, SLA clocks, time totals and a Ctrl/Cmd+K command palette.
AI triage that earns its place
AI proposes type, category, team and priority for each new ticket. It may set category and team by itself only after matching the desk's own decisions 95% of the time over 200 tickets, and it steps back if people start correcting it.
Service levels on business time
Response, update and resolution targets come from the most specific matching policy, run on business calendars rather than the wall clock, and follow an impact-and-urgency priority matrix.
Approvals, rules and workflows
Requests can wait for the requester's line manager, with delegation. Business rules can be dry-run against recent tickets before they go live, and multi-step workflows can wait, ask for approval and raise tasks.
Beyond the IT desk
Installable packs add HR, facilities, finance and legal desks. Major incident, problem, change, CMDB and asset management are built as modules and available through the API.
Separate, auditable tenants
Each organisation's data is isolated by PostgreSQL row-level security and a tenant-aware data client, checked by release-blocking isolation and permission test suites, with a tamper-evident audit trail.
Works on a patchy connection
The portal and workbench install as apps and stay readable offline. Reporting an issue, adding a comment and deciding an approval are queued safely until the connection returns.
See it in action.
Screens captured from the working application using fictional demo data.
How a ticket moves through the platform
- 01
Raised anywhere
A requester uses the portal, sends an email or messages from a chat tool. The ticket, its audit entry and its event are written in one database transaction, whichever door it came through.
- 02
Triaged and given targets
Business rules run, the matching SLA policy starts its clocks on the right business calendar, and AI triage runs afterwards in the background, so a slow model never holds up intake.
- 03
Worked in the workbench
The queue refreshes as tickets change. An agent accepts or dismisses the triage suggestion, replies to the requester or adds an internal note, and logs time against the work.
- 04
Approved and fulfilled
Requests that need approval wait for the line manager, who can decide from a phone. A fulfilment workflow then raises the task and tells the requester.
- 05
Resolved and learned from
On resolution the AI's answer is scored against what the desk actually chose. A default workflow closes the ticket five days later unless the requester reopens it.
How it's built.
People reach the service desk through three web applications and several messaging channels, and all of them talk to the same API and the same ticket record. Slower or external work, such as notifications, SLA timers, search indexing and AI decisions, runs in a background worker so the screens stay fast.
Technically, it is a TypeScript modular monolith in a pnpm and Turborepo workspace: a Fastify API and a BullMQ worker mount the same 27 domain modules, each owning its own tables in PostgreSQL 16 through Prisma. A boundary check in CI keeps modules out of each other's tables, events leave through a transactional outbox, and the three Next.js applications use a backend-for-frontend so the browser never holds an access token.
- Requester portalNext.js 16 PWA: catalogue, tickets, approvals, knowledge
- Agent workbenchNext.js 16 PWA: live queue and three-pane ticket workspace
- Admin consoleNext.js 16: SLAs, rules, catalogue, AI triage, audit
- Inbound channelsEmail, Slack, Teams, WhatsApp and completed-call webhooks
- Backend-for-frontendOpaque __Host- session cookie; tokens stay server-side
- REST APIFastify 5, versioned /api/v1, 383 routes, problem-details errors
- IdentityKeycloak OIDC broker, SCIM provisioning
- Real-time updatesServer-sent events fed by Redis pub/sub
- 27 domain modulesTicket, SLA, rules, workflow, approvals, catalogue, knowledge, CMDB, AI and more
- Background workerBullMQ: 16 queues in four families (events, engine, comms, data)
- AI gatewayJEV decision engine for triage, Claude for drafting; budgets, prices, residency
- Integration gatewaySingle way out: address guard, circuit breaker, SigV4 signing
- PostgreSQL 16184 Prisma models; RLS forced on 175 tables; outbox; hash-chained audit
- RedisQueues, sessions, caches and pub/sub
- SearchPostgreSQL full-text by default; Meilisearch optional
- AttachmentsDirect uploads via short-lived presigned URLs
- Railway, EU WestDeploy pipeline for multi-target Docker images in phase order
- GitHub ActionsTypecheck, unit, integration, isolation, gitleaks, actionlint, Trivy
- OpenTelemetryTraces across HTTP, PostgreSQL and Redis
Technology
Frontend
- Next.js 16 (App Router)
- React 19
- TypeScript 5
- Design tokens with five themes
- Service worker and IndexedDB outbox
Backend
- Node.js 22
- Fastify 5
- Zod
- BullMQ 5
- Prisma 6
- Nodemailer
Data
- PostgreSQL 16 with row-level security
- PostgreSQL full-text search and pg_trgm
- Redis
- Meilisearch (optional)
- Presigned direct uploads
AI & ML
- Anthropic Claude (official SDK)
- TypeSafe JEV decision engine
- Versioned prompt registry
- Per-tenant AI budgets
- Offline stub provider
Integrations & APIs
- Versioned REST API
- Typed TypeScript SDK
- Slack
- Microsoft Teams and Microsoft Graph
- Voice call webhooks
- Postmark
- SCIM provisioning
- Outbound webhooks
Auth & Security
- Keycloak (OIDC)
- jose JWT verification
- Scoped role-based access
- SHA-256 hash-chained audit trail
- Gitleaks secret scanning
Hosting & DevOps
- Railway deploy pipeline
- Docker multi-target builds
- GitHub Actions
- Trivy image scanning
- pnpm 10 and Turborepo
- OpenTelemetry
Testing & Quality
- Vitest 3
- PostgreSQL and Redis integration suites
- Tenant-isolation and permission suites
- axe-core and jsdom component tests
- WCAG contrast checks on design tokens
- Module boundary check
The hard parts, solved.
- 01
Letting AI act without losing control
Triage is treated as a decision, not generated text: a provider answers typed questions with a confidence for each, and every decision is stored with its provider, model, latency and cost. Triage asks the JEV decision engine and, if it is outside the tenant's permitted regions, unpriced, over budget or down, the ticket simply keeps what intake gave it. The auto gate is read from that tenant's own record, and if people correct more than 5% of the last 100 applied answers, the platform drops back to suggest mode, audits the change and emails the administrators.
- 02
Tenant isolation that is tested, not assumed
Row-level security is forced on every tenant table, and the migration script counts them on each run. The application connects through separate database roles for the table owner, the application, platform operations and read-only reporting, and a tenant-aware data client adds a second layer. The seed deliberately creates two tenants with identical ticket numbers and email local parts, so the release-blocking isolation suite would catch a leak that different-looking data might hide.
- 03
Reliable events inside a modular monolith
Each state change writes its domain event to an outbox table in the same transaction, and consumers record what they have processed so a redelivery does no harm. Search documents and reporting data are projections that can be replayed from the event log. Because modules cannot reach into each other's tables, moving one into its own service later is a deployment change rather than a rewrite.
- 04
Service levels that are fair to everyone
SLA clocks run on business calendars, pause while a ticket waits on the requester, a third party or an approval, and resume when it moves on. When AI triage in auto mode sets a ticket's category or team, the SLA module re-matches the policy but the clock keeps its original start, so a ticket never gains extra time because it was classified late.
- 05
Safe sessions and deliberately limited offline writes
A shared backend-for-frontend package gives each app a server-side session behind an opaque __Host- cookie, rebuilds every proxied path from validated segments, forwards only allow-listed headers and checks the origin on writes, so no access token reaches the browser. Offline support is limited on purpose to three additive actions (report, comment, approve), because replaying anything that depends on the current state of a ticket could be wrong by the time it arrives.
A service-desk MVP with an enterprise core
Phases 1 and 2 (foundation and the service-desk MVP) are built and verified, and Phases 3 and 4 (workflow, knowledge and search, then enterprise ITSM) are built. Phase 5 has begun with the requester portal, the agent workbench and a first slice of the admin console.
The platform runs end to end with fictional data, all 1,564 unit tests pass, and CI runs the integration, tenant-isolation and permission suites against real PostgreSQL and Redis. A Railway deployment pipeline with image scanning is in place, but no public production address has been published, and several capabilities are still reachable only through the API until their screens are built.
- UX/UI design
- Software architecture
- Full-stack development
- AI engineering
- DevOps