ClientFlow is the final project developed for 4Geeks Academy by a team of four Full Stack developers.
It is an AI-powered multilingual CRM and customer operations platform designed to centralize client management, conversations, appointments, jobs and intelligent automation.
- Lead and client management
- Appointments and job tracking
- Conversation inbox and channel adapters
- AI agent orchestration
- RAG-based knowledge system
- Conversation memory
- User roles and permissions
- Authentication and password recovery
- Multilingual interface: English and Spanish
- React
- JavaScript
- REST API integration
- Python
- Flask
- SQLAlchemy
- PostgreSQL
- JWT
- Password hashing
- Password reset flow
- LLM API
- RAG
- Vector embeddings
- External channel integrations require provider configuration
🚧 In development
- Carlos Alberto — Full Stack Developer / Technical Lead
- Eudald — Full Stack Developer
- Jesus — Full Stack Developer
- Marian Mircea — Full Stack Developer
The src/front directory contains the React interface, src/api the Flask API, and tests the automated tests. Technical documentation and architecture sources are linked below.
- Python 3.13 and Pipenv.
- Node.js 20 or later and npm.
- Git.
- A configured database. Shared deployment uses PostgreSQL; some local development and automated tests use SQLite.
Run these commands from the repository root:
pipenv sync
npm ciThese commands install the versions recorded in Pipfile.lock and
package-lock.json. They do not create the database or start the application.
After configuring the environment and preparing the database, use two terminals.
Backend:
pipenv run startFrontend:
npm run startThe frontend uses port 3000 and the backend uses port 3001.
Vite includes an /api proxy targeting http://127.0.0.1:3001 by default.
BACKEND_PROXY_TARGET can override that internal target.
In Codespaces, open the forwarded frontend address for port 3000.
A browser request to localhost refers to the user's computer, not the
remote Codespace.
If .env does not exist, copy .env.example to .env.
Keep any existing configuration and never commit real credentials.
Configure these values:
| Variable | Purpose |
|---|---|
DATABASE_URL |
Connection URL for your own database. The example PostgreSQL URL must match your environment. |
FLASK_APP |
Set to src/app.py. |
FLASK_DEBUG |
Use 1 locally and 0 in production. |
JWT_SECRET_KEY |
A private, randomly generated signing secret. |
VITE_USE_MOCK_API |
Set to false to use the backend. |
VITE_BACKEND_URL |
API server base address, without /api or /api/login. |
FRONTEND_ORIGIN |
Exact frontend origin allowed by the backend. |
AUTH_RESET_URL |
Frontend password-reset page address. |
ENABLE_DEV_ADMIN |
Keep 0 unless explicitly enabling the local development admin. |
Generate a JWT signing secret locally:
python3 -c "import secrets; print(secrets.token_hex(32))"Store the result only in your private environment configuration.
For local development, VITE_BACKEND_URL can be http://localhost:3001.
When frontend and backend share the same origin, it can be omitted or left
empty; requests then use relative /api addresses. In Codespaces, Vite proxies
these requests to the backend inside the Codespace.
When using the same-origin proxy, the backend port does not need to be public.
Restart the development servers after changing .env.
For a new empty database, configure DATABASE_URL and JWT_SECRET_KEY, then run:
pipenv run flask db upgrade
pipenv run flask seed-plansRevision 6bb753c896ae creates the current schema. Roles are enum values in
company memberships, not a separate catalog requiring seed rows. Plans are
seeded separately; the protected browser seed remains available.
On a disposable database, pipenv run flask db downgrade base removes the
schema and its data; pipenv run flask db upgrade recreates it. Never use
this rollback on a database whose data must be preserved.
Before updating a database that contains data:
- Make a backup and verify restoration to a separate database.
- Inspect the restored copy's schema and recorded Alembic revision.
- Compare the schema with the current models and review the required changes.
- Test the update on that copy, including checks that existing records survive.
- Apply the reviewed procedure to the shared database only after validation.
Do not run the initial migration against existing tables. Do not use
flask db stamp to hide differences: it only records a revision, without
updating the schema. Databases created by bootstrap or older migrations need
individual reconciliation; this ticket does not provide an automatic legacy
conversion. The closed PR #42 is not part of the current migration chain.
Local validation on PostgreSQL 16 passed upgrade, schema comparison, plan seeding without duplicates, downgrade and re-upgrade. The cycle test repeats creation, seeding and rollback twice in a newly created disposable database.
Prepare a dedicated PostgreSQL database named clientflow_db43. Its test user
must have permission to create databases. Set MIGRATION_TEST_DATABASE_URL
to its connection URL in your terminal; do not use production credentials.
For the local socket setup used during development, in the same terminal:
export MIGRATION_TEST_DATABASE_URL="${PG43_URL:?Set PG43_URL to the dedicated test database URL}"From the project root, with FLASK_APP=src/app.py and a valid test
JWT_SECRET_KEY configured, run:
PIPENV_DONT_LOAD_ENV=1 DATABASE_URL="${MIGRATION_TEST_DATABASE_URL:?Set the test database URL}" pipenv run flask db upgrade
PIPENV_DONT_LOAD_ENV=1 DATABASE_URL="${MIGRATION_TEST_DATABASE_URL:?Set the test database URL}" pipenv run flask db check
PIPENV_DONT_LOAD_ENV=1 MIGRATION_TEST_DATABASE_URL="${MIGRATION_TEST_DATABASE_URL:?Set the test database URL}" PYTHONPATH=src:tests pipenv run python -m unittest test_postgres_migrations test_postgres_migration_cycle -vPIPENV_DONT_LOAD_ENV=1 prevents Pipenv from replacing the selected test URL
with the database URL in .env. The schema test reads the prepared database.
The cycle test creates and removes only its own uniquely named database.
Without MIGRATION_TEST_DATABASE_URL, both PostgreSQL tests are skipped.
GitHub Actions is configured to run these checks with a temporary PostgreSQL 16 service within the required Backend tests job. Confirm that job passes on the PR before merging; local results do not prove the hosted run succeeded.
The following bootstrap is an alternative for local demo accounts, not a step
to run after db upgrade:
For a new, empty, disposable local database only:
- Configure
DATABASE_URLfor that database. - Set
FLASK_DEBUG=1andAUTH_ALLOW_LOCAL_BOOTSTRAP=1locally. - Configure
JWT_SECRET_KEYand create the SQLite parent directory if needed. - Run:
pipenv run flask auth-local-bootstrapFollow the prompts to create the local owner account. The command refuses databases that already contain tables. It creates the current model tables and initial demo records; it does not migrate an existing database.
Do not delete an existing database to bypass this check. Back up existing data and coordinate schema updates with the team.
Create missing catalog plans with pipenv run flask seed-plans, or use the
protected /api/seed-plans browser form described below when no terminal is
available. Neither method restores deleted customer records.
See authentication setup for local bootstrap details. Shared PostgreSQL deployment must use the versioned migration chain and validate it against the target environment before release.
AI features require reachable embedding and response services. The example private service address is not a public endpoint. When using the Mac Mini through Tailscale, the machine running the backend must have authorized network access to it. This also applies to Codespaces.
Configure these backend-only variables:
KNOWLEDGE_EMBEDDINGS_URL,KNOWLEDGE_EMBEDDINGS_API_KEY,KNOWLEDGE_EMBEDDINGS_MODELandKNOWLEDGE_EMBEDDINGS_DIMENSIONS.AI_SERVICE_URLandAI_SERVICE_MODEL.- For the default
companyauthentication mode, useAI_SERVICE_COMPANY_KEYSto map ClientFlow company IDs to provisioned service credentials. For a single company, useAI_SERVICE_COMPANY_IDwithAI_SERVICE_API_KEY.
Set AI_SERVICE_AUTH_MODE=platform_stateless explicitly to select this mode.
The platform_stateless mode uses AI_SERVICE_PLATFORM_KEY and requires
a verified stateless inference service. ClientFlow must continue to enforce
company authorization and select only that company's authorized context.
Do not enable this mode for a service that retains shared conversation state.
The example embedding configuration uses embeddinggemma with 768 dimensions.
The configured model and dimensions must match the service and stored vectors.
To prepare an AI demonstration:
- Upload and successfully process a document for the selected company.
- Create an agent and link its authorized documents.
- Assign the agent to a conversation.
- Generate a draft, review its sources, and approve or reject it.
Missing configuration or service failures may require human attention. A running CRM does not by itself confirm that the AI service is reachable. Never place service credentials in frontend variables.
See knowledge processing and AI orchestration for the detailed configuration and limitations.
| Method and path | Purpose |
|---|---|
GET /api/plans |
List active plans. |
POST /api/register |
Register an account and company. |
POST /api/login |
Obtain an access token. |
GET /api/me |
Read user memberships. |
GET /api/auth/context |
Validate company access. |
GET /api/clients |
List clients. |
POST /api/clients |
Create a client. |
GET /api/leads |
List leads. |
POST /api/leads |
Create a lead. |
POST /api/leads/<id>/convert |
Convert a lead into a client. |
GET /api/conversations |
List conversations. |
GET /api/conversations/<id>/messages |
Read messages. |
POST /api/conversations/<id>/messages |
Send an inbox message. |
GET /api/knowledge/documents |
List documents. |
POST /api/knowledge/documents |
Upload a document. |
POST /api/knowledge/documents/<id>/process |
Process a document. |
Protected company routes require Authorization: Bearer <token> and X-Company-ID: <id>. The server validates membership; additional permissions depend on the action.
- System architecture / Arquitectura
- Database model / Modelo de datos (DBML)
- MVP scope / Alcance
- Authentication / Autenticación
- Registration / Registro
- Members / Miembros
- Appointments / Agenda
- Conversations / Conversaciones
- Channels / Canales
- Knowledge / Conocimiento
- AI / IA
Use a fictional company and rehearse the complete flow in the environment being presented. One person shares the screen; divide the following three blocks among the speakers.
- Access and product: introduce the problem, team and stack; show plans, sign in with the demo account and explain the selected company.
- Client operations: create a fictional lead, convert it into a client, open its details and show a previously verified appointment. Show jobs only if that version's flow works with real backend data.
- Assisted support: show the processed document and agent; open a web conversation, receive “Hi, I want to replace my wardrobe”, generate a draft, review sources, approve it and verify delivery. Continue with a second question to demonstrate context.
Before rehearsal, check the active subscription, permissions, processed documents and backend access to AI. Also verify the participant's web-chat access; an administrator account is not a substitute for that session. If AI fails, demonstrate manual support and explain the limitation; do not present a prepared response as live generation.
No shared password is published in the repository. Create a fictional account through registration or the local bootstrap documented above. The bootstrap prompts for a 12–128-character password and creates a three-day trial. Share credentials privately with the team and professor. Verify access before rehearsal; an expired trial blocks protected modules. Do not publish tokens or passwords in slides.
From the repository root:
AUTH_TEST_DATABASE_URL=sqlite:// PYTHONPATH=src:tests pipenv run python -m unittest discover -s tests -p 'test_*.py' -v
node --test tests/frontend/calendar.test.mjs
npm run build- PostgreSQL 16 migration tests passed locally. Existing databases require backup, schema reconciliation and testing on a restored copy; these tests do not certify a production deployment.
- Registration supports simulated payment, not real charges. See the linked registration contract.
- Channel adapters do not demonstrate an active external integration. Do not advertise WhatsApp or email as operational without testing their providers and credentials.
- AI requires reachable services and human review of drafts. Access from the Mac does not guarantee access from Codespaces.
- Verify jobs, dashboard and settings in the version being presented; exclude pending or simulated features from the walkthrough.
- Automated quality checks and the security changes are integrated in this branch. Check the deployed revision separately; a merge does not prove deployment.
- Review secrets, HTTPS, email recovery, backups and data retention before production. This guide does not certify those services.
This project was developed for educational purposes as part of the 4Geeks Academy Full Stack Development program.
After recreating the database tables through migrations, restore the default plans:
pipenv run flask seed-plansThis command creates missing plans without changing existing plans or prices.
It does not restore deleted accounts, clients, or conversations; those require
a database backup. The /api/seed-plans page supports protected browser setup as described below.
Customers can still read active plans through /api/plans.
- Keep credentials in backend environment variables. Never put secrets in
VITE_*variables, source code, screenshots, or logs. - Rotate any credentials that have been shared or exposed.
- Keep database backups and private keys outside the repository.
- Disable debug mode in production and configure the allowed frontend origin.
- Client avatars are generated locally without sending names to an avatar service.
Use fictional data for demonstrations. Before using real customer data, define the privacy notice, applicable consent requirements, retention periods, and procedures for access and deletion requests, including backups.
Automatic retention and deletion are not implemented by this security change. Review the AI service's access controls, logging, and retention separately before sending real customer information.
The review includes automated tests for authentication, tenant isolation, member permissions, uploads, and plan seeding. Passing tests cover the tested scenarios and do not replace a production deployment review.
For hosting without a terminal, open /api/seed-plans on the backend domain.
GET only displays the form. To enable creation, configure PLAN_SEED_KEY in the
hosting environment with a randomly generated secret of 32–512 characters.
Generate it on your own computer with python3 -c "import secrets; print(secrets.token_hex(32))".
Enter it in the password field and click Create plans. The form submits POST;
do not put the key in the URL. Use HTTPS outside local development.
The database tables must already exist. Missing plans are created; existing
prices are preserved. This does not recover deleted accounts or client data.
Remove PLAN_SEED_KEY after setup to disable browser writes. The terminal
command pipenv run flask seed-plans remains available independently.
The shared staging environment is available at:
Render provisions the Flask and React web service together with a PostgreSQL 16
database using render.yaml. The build uses Python 3.13, Pipenv and Node.js 22.
Database migrations run automatically before Gunicorn starts, and the frontend
communicates with the API through the same public origin.
Public deployment settings are stored in render.yaml. Private values such as
JWT_SECRET_KEY and PLAN_SEED_KEY must remain in Render environment variables
and must never be committed.
For a new empty staging database, open /api/seed-plans and provide the private
PLAN_SEED_KEY. Confirm the resulting catalog through /api/plans.
The staging smoke test must verify:
/api/healthreturns a successful response.- The three catalog plans are available.
- A new account can register and sign in.
- Created client data remains available after refreshing and signing in again.
After this staging pull request is merged, configure the Render service to track
the develop branch.