Skip to content
debarko.deproductsphera

Open source · headless CRM

Phera

ফেরা — the return

A headless CRM: configurable funnels, an omnichannel support inbox, and event-driven workflows — as an API you run yourself, not a SaaS seat you rent. No product UI ships in the box. Bring your own client, or drive it from a script.

A CRM is infrastructure, not a seat

Most teams end up with rich operational software and still no relationship layer: a lead table scoped to one channel, no support ticketing, and a customer's calls, tickets and deals scattered across tables with no timeline tying them together.

The deeper problem is that one person is not one journey. The same contact can fill three different forms over three months, each a different commercial motion run by a different team. Collapsing that onto a single "lead status" field loses two of the three journeys. Phera keeps the person and the journey as separate records from day one.

Vertical-agnostic core
No hospital, no e-commerce, no SaaS vocabulary baked into the domain model. Contact, Pipeline, Deal — map your vertical on top.
Config over code
Funnels, stages, ownership rules and workflows are data. Adding one is an admin screen, not a pull request.
Pluggable everything
Telephony, email, messaging and lifecycle/CDP providers sit behind interfaces. The core never imports a vendor SDK.

One person is not one journey

A Contact can hold many Deals across many Pipelines at once. Process variation lives on the Pipeline; people live on the Contact. There is no Contact.stage — only a list of Deals, each with its own owner and position.

Contactone personowner_user: nullivf_consultNewCounseling bookedVisited● Deal · owner: sales AWonskin_consultNew● Deal · owner: sales BConsult bookedPlanWonpartner_referralNewIn progressQualified● Deal · owner: partner deskWonstage lives on the Deal, never on the Contact
Contact
A person. The unified-timeline anchor — not a lead.
Pipeline
A funnel: named, ordered stages. Admins create these, no deploy.
Stage
One step in a Pipeline — open, won or lost category, optional SLA.
Deal
One Contact's membership in one Pipeline. This is the lead.
Ticket
A support request. One inbox regardless of channel.
Interaction
Append-only row behind the contact-facing timeline.
AuditEvent
Immutable record of every mutation — who, what, when, from → to.
Workflow
A published graph: trigger → conditions → wait/branch → actions.

Same binary, two roles

One image, two commands. phera api serves HTTP and WebSocket; phera worker drains queues; phera all runs both on a laptop. Scaling out is more boxes of the same image, never a new microservice. Adapters — messaging, email, voice, lifecycle — are the only thing that changes per deployment.

Your clientcurl · a web app · another serviceREST / WebSocketsame image — phera all runs both, one boxphera apiHTTP / WSphera workerqueue consumersPostgresRedisOTelcollectorMessagingGallabox / WhatsAppEmailGoogle GroupVoiceAcefone, Exotel nextLifecycleMoEngage, CleverTap, webhookadapters —core imports zero vendor SDKs
  • FastAPI
  • SQLAlchemy (async)
  • Postgres
  • Redis
  • OpenTelemetry
  • Alembic
  • Typer CLI
  • Docker

Every mutation writes its own history

All writes go through one helper. It updates the row, appends an immutable AuditEvent and an OutboxEvent in the same transaction, then commits. Nothing is lost if Redis is briefly down — the broker is notified only after commit, and a dispatcher heals any miss. A nightly job folds the log into small daily tables, so aging and TAT numbers come from the same database, not a warehouse.

mutate(entity, patch, actor)same transaction1 · row update2 · AuditEvent (append-only)3 · OutboxEvent (pending)rolls back together, or not at allCOMMITafter commit,best-effortnotify brokerworker drains queueworkflow runs + lifecycle fan-outWhatsApp · email · SMS · MoEngage, CleverTap…once a day,off-peaknightly rollup(day)MetricDaily · AgingSnapshotDailydashboards read these, never the raw logone write path, two clocks: automation reacts in seconds, analytics folds in once a day

What's in the box

Configurable funnels

Pipelines and stages are admin-owned objects — create, clone, reorder without a deploy. One Contact can sit in several at once.

Omnichannel inbox

Email, WhatsApp and phone normalize into one Ticket. One routing engine, one capacity pool, L1 → L2 overflow.

Workflow engine

n8n-style graphs react to domain events and time-based waits, executed asynchronously on horizontally scaled workers.

Call transcription

Inbound and outbound calls through a connected provider are recorded, transcribed, and attached to the contact timeline.

Configurable ownership

Contact-centric or pipeline-centric — a workspace setting decides who owns what, not a fork of the codebase.

Audit trail as analytics

Every mutation writes its own history. A nightly job folds it into daily metrics — no warehouse, no CDC stream.

Run it locally

Docker for Postgres, Redis and the OTel collector; the API and worker run as one process on a laptop. API docs are served straight off the running app.

API + docs
localhost:8000/docs
Health
localhost:8000/health
Auth
X-Actor-* headers only — no JWT baked into the service.
git clone https://github.com/debarko/phera.git
cd phera

docker compose up -d postgres redis otel-collector
pip install -e ".[dev]"
cp .env.example .env
alembic upgrade head

phera all

Tests split into unit (no database) and integration (ephemeral in-memory SQLite, created and destroyed per test) — no Docker, Postgres or Redis required for CI.

make test              # fast, no DB
make test-nightly      # exhaustive regression suite