Architecture
How ZapTicket's components fit together.
System overview
ZapTicket consists of four deployed components, each serving a distinct role:
Domain layout
zapticket.app → Marketing site (public landing pages)
app.zapticket.app → Dashboard (agent inbox, auth-gated)
api.zapticket.app → Backend API (REST + SignalR hub)
cdn.zapticket.app → Widget bundle (static JS file)
docs.zapticket.app → Documentation (this site)Backend (ASP.NET Core)
The backend is a .NET 10 Web API that serves REST endpoints and a SignalR hub for real-time communication. It connects to PostgreSQL for persistence and uses JWT for authentication.
- REST API — CRUD for tenants, agents, conversations, tickets, settings
- SignalR Hub — real-time messaging at
/hubs/chat - EF Core — code-first migrations, global tenant query filters
- Multi-tenancy — every query scoped by
tenant_idvia global filters
Dashboard (Next.js)
The agent-facing SPA built with Next.js App Router, React, and Tailwind CSS. Connects to the backend via REST for data and SignalR for real-time updates.
- Conversation inbox with live updates
- Ticket management
- Settings (branding, keys, agents, canned responses)
- Workspace switcher for multi-workspace users
Widget (TypeScript bundle)
A single self-contained JavaScript file (~20KB gzipped) that embeds on customer websites. Uses Shadow DOM for complete CSS isolation — no styles leak in or out.
- Connects to SignalR via site key authentication
- Fetches branding from the API on load
- Falls back to a ticket form when no agents are online
- Supports signed visitor identity (HMAC)
Data flow
Visitor (widget) ─── WebSocket (site-key auth) ──→ SignalR Hub
│
Agent (dashboard) ── WebSocket (JWT auth) ──────────────→│
│
↓
PostgreSQL
(tenant-isolated)Authentication model
ZapTicket uses a two-level JWT system:
- User token (scope: user) — issued on login, used for workspace listing/creation
- Workspace token (scope: workspace) — issued on workspace select, includes
tenant_id,agent_id,role
The widget authenticates via the X-ZapTicket-Site-Key header or ?site_key= query parameter.