Case study
Education integration platform
FastAPI services that keep Zoho CRM in sync with student information systems, SOAP services and legacy databases
One FastAPI service per institution connects Zoho CRM with the systems that institution already runs, from Oracle PeopleSoft Campus Solutions over SOAP to SQL Server databases behind a VPN. I build, deploy and watch them.

The problem
Universities and business schools in Latin America and Spain run admissions in Zoho CRM, while programs, prices, payments and enrollments live in their academic systems. Without an integration, admissions teams copy that data by hand and the CRM drifts away from what actually happened.
No two institutions run the same stack. One exposes Oracle PeopleSoft Campus Solutions over SOAP with WS-Security, another a SQL Server database reachable only through a VPN, others drop nightly CSV files on FTP or offer REST APIs.
Constraints
- Zoho CRM enforces API limits, and its bulk jobs finish asynchronously.
- The source systems belong to the institutions. I cannot change them, and some are reachable only through a VPN.
- Three people in the GitHub organization run more than 20 services.
- The data belongs to applicants and students from several countries, Spain among them.
What I built
- One FastAPI service per institution on a shared standard, with routers without logic, module controllers that normalize and compose, and one gateway layer per external system.
- Long syncs answer 202 Accepted and run as background jobs. Each run has an ID, a status that moves from pending to finished or failed, and a record in the CRM with its outcome, duration and report.
- Writes to Zoho go through the Bulk API or batched REST calls. Run records are found with COQL, because the search index lags behind new records and would create duplicates.
- Logs have five levels, and the level can rise to full SOAP and HTTP dumps at runtime without restarting the container.
- Each service ships as a hardened Docker image on GHCR and deploys with Docker Compose from GitHub Actions. OpenTelemetry traces go to Tempo, metrics to Prometheus and logs to Loki.
Architecture
How the services connect
Data moves both ways between neighboring columns. The row below runs under every service.
Systems each institution runs
- PeopleSoft Campus Solutions over SOAP with WS-Security
- SQL Server databases over a VPN
- Nightly CSV files on FTP
- REST APIs and webhooks
One FastAPI service per institution
- Routers, module controllers and one gateway per external system
- Background jobs that answer 202 Accepted
- A run record in the CRM
Zoho CRM
- OAuth 2.0
- Bulk API and batched REST
- COQL lookups
Operations
- GitHub Actions, GHCR images and Docker Compose
- OpenTelemetry traces in Tempo, metrics in Prometheus, logs in Loki
- Grafana alerts to Telegram
Decisions and trade-offs
One service per institution
No two institutions run the same stack, and each one has its own credentials.
202 Accepted instead of waiting
A catalog sync can take minutes, longer than a CRM function or a browser will wait.
Written debt
The first service predates the standard, and newer services could copy its differences.
Results
- More than 20 services in production for more than 15 institutions in Latin America and Spain.
- The newest services implement the full standard, with idempotency keys, a concurrency limit with backoff when Zoho answers 429, jobs persisted in PostgreSQL and secrets encrypted with AES-256-GCM.
- One observability stack watches all of them, with 4 Grafana dashboards, 18 uptime probes and 12 alert rules that reach Telegram.
Open items
- Bring the older services up to the standard, starting with idempotency keys and backoff.
- Contract tests for each external system in CI. Today only the newest services have a test suite.
- Publish the sync volume and uptime of each service from the Prometheus data.
Stack
Pick a technology to see the projects that use it.




