Architecture
Ringee is composed of 3 main services backed by a set of external services. The main services communicate through HTTP and Temporal, and connect to external services for data storage, telephony, auth, and payments.- Frontend — Web interface for managing calls, contacts, and analytics.
- Backend — REST API that handles all business logic and integrations.
- Orchestrator — Temporal worker that runs durable background jobs and schedules.
Frontend
A single Next.js application on port4200, using the App Router with feature-based organization:
- the marketing site;
- the authenticated dashboard — calls, contacts, campaigns, analytics, numbers, billing;
- the browser-based dialer, powered by Telnyx WebRTC.
Backend
The backend is the brain of Ringee — a NestJS application on port3000 with a global /api prefix. It:
- handles all REST API requests, protected by a Clerk auth guard;
- manages Telnyx call control, number purchasing and WebRTC credentials;
- processes Stripe webhooks for payments, credits and subscriptions;
- handles Clerk webhooks for user and organization management;
- serves the Public API for CRM integrations;
- serves the MCP server for AI assistants;
- serves the Dialer SDK endpoints under
/api/v1/sdk; - starts durable workflows on Temporal.
Orchestrator
A separate process (apps/orchestrator) that runs the Temporal worker. It executes the workflows the backend starts and owns the periodic schedules:
- webhook delivery drains and retries;
- callback, reminder and retry pollers;
- CRM synchronization drains;
- recording and transcription processing;
- push notifications via Firebase Cloud Messaging.
Temporal replaced the earlier Redis pub/sub worker. Redis is still used for caching and session state.
Shared packages
Ringee is a pnpm workspaces monorepo. Business logic lives in shared packages rather than in any single app:
This is why the Public API, the MCP server, the CLI and the Dialer SDK all behave identically: they are different entry points into the same service layer.
External Services
Multi-tenancy
Users belong to organizations through memberships. Most entities — calls, contacts, numbers, credits, campaigns — carry both auserId and an optional organizationId, so a user’s personal data and their organizations’ data stay separate.
Every entry point respects this. The dashboard, the Public API, the MCP server and the Dialer SDK all resolve the same ownership context before reading or writing.
Building on Ringee
Ways to build
Which integration surface fits your use case
Add a provider
Integrate another telephony provider

