ERP Platform for K12 Schools
2025 — PresentMulti-tenant ERP connecting HR, payroll, and staff provisioning for K-12 school districts. Replaces manual clock-in, paper-based hiring workflows, and IT-gated access changes. Serves 1,650+ daily active users at the 7th largest Illinois school district.
Backend Engineer @ APS Data Technologies
Problem
- Districts were running HR, payroll, and staff provisioning across disconnected systems: background checks in one vendor portal, payroll setup in another, timesheets on paper. There was no single place to see a candidate or employee's full status.
- Access control was binary: a role either had module access or it didn't. Districts with 50+ role types and constantly shifting responsibilities had no way to express that without developer intervention and a redeploy.
- Candidate screening was fully manual. HR staff reviewed applications one by one, which took 2+ hours per batch and became a bottleneck every time a district tried to expand hiring.
What I Built
- Async hiring pipeline (Django + Celery + Redis) with idempotent retry logic and a dead-letter queue that cut candidate screening from 2 hours to 12 minutes.
- Capability-based RBAC following module.submodule.action, deny-by-default. A central permission registry materializes into the database on deploy and is the single source of truth; stale grants are purged on every update.
- Tool-calling AI agent connected to background-check and payroll APIs via tool calls, with per-user memory persisting each person's recurring queries across sessions.
- REST API powering a Next.js 15 + React 19 frontend with Zustand-managed candidate state. DocuSign integration for offer signatures, SSO for district-wide auth.
- Timesheet and onboarding modules integrated with each district's existing payroll system via nightly exports and API calls, so no district had to replace infrastructure they already ran.
Architecture Decisions
- Celery workers for the hiring pipeline keep expensive screening operations off the API request path. Idempotent tasks with a dead-letter queue mean a Redis blip doesn't silently drop a candidate.
- RBAC modeled as capabilities rather than roles-with-flags, similar to Kubernetes resource access. A missing grant means denied with no separate deny rules to maintain, which made the permission model auditable and meant districts could reason about it without reading code.
- Permission enforcement mirrored identically on the backend (DRF permission classes) and frontend (route and component gating) so UI state and API access never diverge. Live changes propagate to active sessions within 60 seconds with no re-login.
- AI agent uses tool calls to query live payroll and HR data rather than a knowledge base, because status questions need current records and not a cached snapshot from the last embedding run.
Infrastructure
- Multi-tenant architecture with per-district data isolation enforced at the query layer, so every request is scoped to the caller's tenant and role before any business logic runs.
- AWS (ECS, RDS, S3, Terraform) supporting 1,650+ DAU and 500 AI-processed resumes per day. Autoscaling thresholds and connection pool limits sized to actual load profiles.
- CloudWatch alerting on P99 latency, container logs, and error rates across dev/staging/prod environments.
Impact
- Hiring pipeline cut candidate screening from 2 hours to 12 minutes, which moved HR teams from manual review marathons to same-day decisions.
- Districts manage roles and permissions entirely through the admin UI. A change that previously required a developer and a deploy now takes minutes with no tickets filed.
- AI agent reduced time on routine ERP tasks 5x by pulling from background-check and payroll portals through a single interface instead of three separate logins.
- Replaced paper-based timesheets and manual onboarding steps; new hire data syncs to payroll automatically without replacing existing district infrastructure.
