The FastAPI backend for observe. — a lightweight developer observability platform built for African developers.
- Ingests events, errors, and API request data from the SDKs
- Manages organisations, projects, and API keys
- Runs uptime monitor checks on a schedule with retry logic
- Aggregates raw events into daily stats
- Enforces rate limiting, usage caps, and tier restrictions
- Handles billing via Squad by GTCO with recurring payments
- Sends transactional emails via Resend
- Serves a REST API for the dashboard frontend
- FastAPI — API framework
- PostgreSQL — primary database
- SQLModel — ORM (SQLAlchemy + Pydantic)
- Alembic — database migrations
- Celery + Redis — background jobs and scheduling
- httpx — async HTTP client (uptime checks, GitHub OAuth)
- Resend — transactional email
- Squad by GTCO — payment processing (NGN)
app/
main.py # FastAPI app entry point
config.py # Settings via pydantic-settings
database.py # Async engine and session
dependencies.py # Reusable FastAPI dependencies (auth, api key, rate limiting)
redis_client.py # Async Redis client
models/ # SQLModel table definitions
schemas/ # Pydantic request/response models
routers/ # HTTP route handlers
auth.py # Register, login, refresh, logout, password reset
github_auth.py # GitHub OAuth flow
projects.py # Project CRUD
api_keys.py # API key management
events.py # Event and identify ingestion
errors.py # Error capture and dashboard
monitors.py # Uptime monitor management
stats.py # Analytics stats and event explorer
api_monitoring.py # API request ingestion and stats
billing.py # Squad billing, webhooks, subscriptions
admin.py # Internal admin stats endpoint
services/
auth.py # Password hashing, JWT, token utilities
email.py # Resend email templates and sending
rate_limit.py # Redis-based rate limiting
squad.py # Squad API client
tier.py # Tier enforcement logic
usage.py # Monthly usage tracking
workers/
celery_app.py # Celery instance and beat schedule
tasks.py # Background tasks
alembic/ # Migration files
- Python 3.11+
- PostgreSQL
- Redis
git clone https://github.com/yourname/observability-backend.git
cd observability-backend
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txtcp .env.example .envFill in your values — see environment variables reference below.
alembic upgrade headIf a migration fails with
NameError: name 'sqlmodel' is not defined, open the generated migration file and addimport sqlmodelat the top. If a migration fails withNotNullViolation, addserver_default='value'to the column definition in the migration file.
uvicorn app.main:app --reloadAPI running at http://localhost:8000.
Interactive docs at http://localhost:8000/docs.
celery -A app.workers.celery_app worker --loglevel=infocelery -A app.workers.celery_app beat --loglevel=info| Task | Schedule | Description |
|---|---|---|
aggregate_daily_stats |
Every hour (:00) |
Rolls up raw events into daily_stats |
aggregate_endpoint_stats |
Every hour (:30) |
Rolls up API requests into endpoint_stats |
run_monitor_checks |
Every 60 seconds | Pings all active uptime monitors |
process_renewals |
Daily at midnight UTC | Charges subscriptions due today, handles grace periods and downgrades |
enforce_data_retention |
Daily at 2am UTC | Deletes data older than tier retention limit, cleans up stale tokens |
Tracks events, page views, and visitors. Raw events stored in events table, aggregated hourly into daily_stats for fast dashboard queries.
Pings URLs on a configurable interval (1, 5, or 15 minutes). Creates incidents after 3 consecutive failures. Sends email alerts on down and recovery. Pro/Team tier only for alerts.
Captures frontend JS errors and backend exceptions. Groups identical errors by type and message fingerprint. Tracks occurrence count, affected users, and stack traces.
Logs HTTP requests via middleware. Aggregates per-endpoint stats including request count, error rate, average response time, and P95 latency.
All ingestion endpoints are rate limited per API key per minute:
| Tier | Events | Errors | API Requests |
|---|---|---|---|
| Hobby | 100/min | 50/min | 100/min |
| Pro | 1,000/min | 500/min | 1,000/min |
| Team | 5,000/min | 2,000/min | 5,000/min |
Auth endpoints are rate limited to 5 attempts per 15 minutes per IP.
| Feature | Hobby | Pro | Team |
|---|---|---|---|
| Projects | 3 | Unlimited | Unlimited |
| Monitors | 5 | 50 | Unlimited |
| Min interval | 5 min | 1 min | 1 min |
| Events/month | 50,000 | 2,000,000 | 10,000,000 |
| Errors/month | 1,000 | 50,000 | Unlimited |
| API requests/month | 10,000 | 500,000 | Unlimited |
| Retention | 7 days | 90 days | 1 year |
| Email alerts | No | Yes | Yes |
| Method | Path | Description |
|---|---|---|
| POST | /ingest/events |
Track an event |
| POST | /ingest/identify |
Link visitor to user |
| POST | /ingest/errors |
Capture an error |
| POST | /ingest/requests |
Log an API request |
| Method | Path | Description |
|---|---|---|
| POST | /auth/register |
Create account and organisation |
| POST | /auth/login |
Login with email and password |
| POST | /auth/refresh |
Refresh access token via cookie |
| POST | /auth/logout |
Revoke refresh token |
| GET | /auth/me |
Get current user |
| POST | /auth/forgot-password |
Request password reset email |
| POST | /auth/reset-password |
Reset password with token |
| POST | /auth/change-password |
Change password (JWT auth) |
| POST | /auth/change-email |
Change email (JWT auth) |
| POST | /auth/delete-account |
Permanently delete account and all data |
| GET | /auth/github/login |
Start GitHub OAuth flow |
| GET | /auth/github/callback |
GitHub OAuth callback |
| Method | Path | Description |
|---|---|---|
| GET/POST | /projects |
List or create projects |
| GET/DELETE | /projects/{id} |
Get or delete a project |
| GET/POST | /projects/{id}/api-keys |
List or create API keys |
| DELETE | /projects/{id}/api-keys/{key_id} |
Revoke an API key |
| GET | /projects/{id}/stats |
Get daily stats |
| GET | /projects/{id}/events |
Event explorer |
| GET/POST | /projects/{id}/monitors |
List or create monitors |
| GET | /projects/{id}/monitors/{id} |
Monitor detail |
| PATCH | /projects/{id}/monitors/{id}/pause |
Pause or resume monitor |
| DELETE | /projects/{id}/monitors/{id} |
Delete monitor |
| GET | /projects/{id}/errors |
List grouped errors |
| GET | /projects/{id}/errors/{id} |
Error detail |
| POST | /projects/{id}/errors/{id}/resolve |
Resolve an error |
| GET | /projects/{id}/api-stats |
Aggregated endpoint stats |
| GET | /projects/{id}/api-requests |
Raw API request log |
| Method | Path | Description |
|---|---|---|
| POST | /billing/initiate |
Start a Squad payment |
| POST | /billing/webhook |
Squad webhook handler |
| POST | /billing/cancel |
Cancel subscription |
| GET | /billing/subscription |
Get current subscription |
| GET | /billing/events |
Billing history |
| Method | Path | Description |
|---|---|---|
| GET | /admin/stats |
Internal metrics — requires x-admin-token header |
| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes | Async PostgreSQL URL (asyncpg) |
SYNC_DATABASE_URL |
Yes | Sync PostgreSQL URL (psycopg2, for Alembic and Celery) |
REDIS_URL |
Yes | Redis connection URL |
SECRET_KEY |
Yes | JWT signing secret — keep this private |
ACCESS_TOKEN_EXPIRE_MINUTES |
No | Default: 60 |
REFRESH_TOKEN_EXPIRE_DAYS |
No | Default: 30 |
GITHUB_CLIENT_ID |
No | Required for GitHub OAuth |
GITHUB_CLIENT_SECRET |
No | Required for GitHub OAuth |
GITHUB_REDIRECT_URI |
No | GitHub OAuth callback URL |
FRONTEND_URL |
No | Frontend URL for OAuth redirect |
RESEND_API_KEY |
No | Required for email sending |
RESEND_FROM_EMAIL |
No | From address for emails |
SQUAD_SECRET_KEY |
No | Required for billing |
SQUAD_PUBLIC_KEY |
No | Required for billing |
SQUAD_BASE_URL |
No | Squad API URL — sandbox or live |
PRO_AMOUNT |
No | Pro plan price in kobo. Default: 500000 (₦5,000) |
TEAM_AMOUNT |
No | Team plan price in kobo. Default: 1500000 (₦15,000) |
ADMIN_TOKEN |
No | Secret token for /admin/stats endpoint |
MIT