Open source · headless CRM
Phera
ফেরা — the returnA 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.
- 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.
- 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.
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.
Built for one vertical at a time, by config
The core has no industry baked in — these are five real shapes the same Contact/Pipeline/Deal model takes on, picked from where people actually search for a CRM.
One buyer, three open pipelines, one agent who doesn't lose the thread
A buyer renting, resale-hunting, and pre-approving for new construction at the same time — without three lead statuses fighting each other.
Two truck rolls become one, because dispatch can see the whole property
A homeowner requests a roof quote, then a gutter cleaning two months later — Phera notices it's the same address before a second truck gets sent.
Auto, home, and life stay three policies and one relationship
The textbook case for "one person, many funnels" — plus the audit trail a compliance team already wishes it had.
A conveyancing client becomes an estate-planning client, and the file trail never breaks
Two matters, two practice groups, one immutable record of every deadline and touch — the audit trail a malpractice-defense file actually wants.
A wholesale account and a loyalty-program shopper turn out to be the same person
One contact, two completely different commercial relationships — tracked without either one overwriting the other.
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 allTests 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