Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Paramount-Intelligence/HR_Monitoring_System/llms.txt
Use this file to discover all available pages before exploring further.
Workforce OS is configured entirely through environment variables, loaded by Pydantic Settings (pydantic-settings) from the process environment or an .env file. This page documents every variable grouped by functional area. Variables marked Required in production will cause a startup RuntimeError if absent or left at an insecure default when APP_ENV=production.
Never copy a local .env file verbatim into Railway. Set only the variables each service needs. Backend secrets (database credentials, SMTP passwords, VAPID keys) must never appear on the Frontend service.
App Core
| Variable | Type | Default | Description |
|---|
APP_ENV | string | development | Runtime environment. Set to production on Railway to enable production-safety guards. Accepted values: development, dev, local, test, production. |
APP_HOST | string | 0.0.0.0 | Host interface the API server binds to. Leave as 0.0.0.0 for Railway. |
APP_PORT | integer | 8000 | Local port the API listens on. Railway overrides this with the injected $PORT variable via the Procfile. |
APP_SECRET_KEY | string | change-me-in-env | Required in production. Secret used to sign JWT access and refresh tokens. Generate with openssl rand -hex 32. The API refuses to start if this is left at any known-insecure default (changeme, secret, etc.) when APP_ENV=production. |
Only APP_SECRET_KEY is used to sign JWTs. Do not set SECRET_KEY or JWT_SECRET_KEY — those variables are not read by the codebase.
Authentication
| Variable | Type | Default | Description |
|---|
ACCESS_TOKEN_EXPIRE_MINUTES | integer | 15 | Lifetime of the short-lived JWT access token in minutes. Recommended production value: 120. |
REFRESH_TOKEN_EXPIRE_DAYS | integer | 7 | Lifetime of the long-lived refresh token in days. Recommended production value: 30. |
ONLINE_PRESENCE_TTL_SECONDS | integer | 90 | A user is considered online if a heartbeat was received within this window (seconds). Stored in Redis. |
AUTH_LOGIN_MAX_ATTEMPTS | integer | 5 | Maximum failed login attempts allowed before the account is rate-limited. |
AUTH_LOGIN_WINDOW_SECONDS | integer | 900 | Sliding window (seconds) for counting failed login attempts. |
AUTH_FORGOT_PASSWORD_MAX_ATTEMPTS | integer | 3 | Maximum forgot-password requests per window. |
AUTH_FORGOT_PASSWORD_WINDOW_SECONDS | integer | 900 | Sliding window (seconds) for forgot-password rate limiting. |
AUTH_RESET_PASSWORD_MAX_ATTEMPTS | integer | 5 | Maximum password-reset requests per window. |
AUTH_RESET_PASSWORD_WINDOW_SECONDS | integer | 900 | Sliding window (seconds) for password-reset rate limiting. |
Database
The API resolves its PostgreSQL connection in two ways: directly from DATABASE_URL, or constructed from the individual PG* component variables. Both are supported, and Railway provides them automatically when the Postgres service is linked.
| Variable | Type | Default | Description |
|---|
DATABASE_URL | string | (none) | Required. Full PostgreSQL connection string, e.g. postgresql://user:pass@host:5432/dbname. Railway injects this as ${{Postgres.DATABASE_URL}}. A postgres:// scheme is normalised to postgresql:// automatically. SQLite URLs are rejected. |
DATABASE_PUBLIC_URL | string | (none) | Public-facing PostgreSQL URL. Used as a fallback when DATABASE_URL contains an unresolved Railway template placeholder. |
PGHOST | string | (none) | PostgreSQL host. Used to construct DATABASE_URL when DATABASE_URL is not set directly. |
PGUSER | string | (none) | PostgreSQL user. |
PGPASSWORD | string | (none) | PostgreSQL password. |
PGDATABASE | string | (none) | PostgreSQL database name. |
PGPORT | string | 5432 | PostgreSQL port. |
The database must be created with UTF-8 encoding to support emoji and Unicode characters in message fields. Railway’s provisioned Postgres uses UTF-8 by default. For self-managed databases: CREATE DATABASE your_db WITH ENCODING 'UTF8' TEMPLATE template0 LC_COLLATE='C' LC_CTYPE='C';
Redis
| Variable | Type | Default | Description |
|---|
REDIS_URL | string | redis://localhost:6379/0 | Full Redis connection URL. Railway injects this as ${{Redis.REDIS_URL}} when the Redis service is linked. Used by Celery as its message broker. |
Frontend & CORS
These variables control how the API identifies and communicates with the Next.js frontend.
| Variable | Type | Default | Description |
|---|
FRONTEND_BASE_URL | string | http://localhost:3000 | Required in production. The canonical URL of the deployed Next.js app. Used in email links and for CORS resolution. Must be an https:// URL in production. |
CORS_ORIGINS | string | * (dev only) | Required in production. Comma-separated list of allowed frontend origins, e.g. https://your-app.up.railway.app. Wildcards (*) are rejected in production — the API refuses to start without explicit origins. FRONTEND_BASE_URL must appear in this list. |
Frontend service variables (Next.js — set before build):
| Variable | Type | Description |
|---|
NEXT_PUBLIC_APP_NAME | string | Display name baked into the client bundle. Example: Workforce Intelligence & Execution OS. |
NEXT_PUBLIC_API_URL | string | Full URL of the Backend API’s /api/v1 prefix, e.g. https://<api>.up.railway.app/api/v1. Baked in at build time. |
NEXT_PUBLIC_WS_URL | string | WebSocket URL pointing to the API, e.g. wss://<api>.up.railway.app/api/v1/ws. Must point at the API host, not the frontend. |
API_PROXY_URL | string | Server-side proxy target for local development. Example: http://localhost:8080/api/v1. Not needed on Railway. |
Email / SMTP
All SMTP variables are optional. When SMTP_HOST is unset, transactional emails (password-reset links, notifications) are silently disabled.
| Variable | Type | Default | Description |
|---|
SMTP_HOST | string | (none) | SMTP server hostname, e.g. smtp.gmail.com. |
SMTP_PORT | integer | 587 | SMTP port. Use 587 for STARTTLS or 465 for SSL. |
SMTP_USER | string | (none) | SMTP authentication username (also accepted as SMTP_USERNAME). |
SMTP_PASSWORD | string | (none) | SMTP authentication password or app-specific password. |
SMTP_TLS | boolean | True | Enable STARTTLS. Also accepted as SMTP_USE_TLS. |
EMAILS_FROM_EMAIL | string | [email protected] | From-address for outbound emails (also accepted as SMTP_FROM_EMAIL). |
EMAILS_FROM_NAME | string | Workforce OS | Display name for the From header (also accepted as SMTP_FROM_NAME). |
Bootstrap Admin
The backend seeds a default admin user on first startup if the email does not already exist in the database.
| Variable | Type | Default | Description |
|---|
BOOTSTRAP_ADMIN_EMAIL | string | [email protected] | Email address of the auto-created admin account. |
BOOTSTRAP_ADMIN_PASSWORD | string | change-this-password | Required in production. Initial password for the bootstrap admin. The API refuses to start in production if this is left at change-this-password, changeme, or password. Change this after the first login. |
BOOTSTRAP_ADMIN_NAME | string | HR Admin | Display name of the bootstrap admin user. |
BOOTSTRAP_ADMIN_PASSWORD is provided as plain text for the initial setup only. Change it immediately after the first successful login via the user profile settings page. The production startup validator will reject the default value.
Storage — Profile Images
| Variable | Type | Default | Description |
|---|
PROFILE_IMAGE_STORAGE | string | local | Storage driver for profile pictures. Accepted: local, s3, railway_bucket, railway. Use s3 with the S3 variables configured below for production. |
PROFILE_IMAGE_UPLOAD_DIR | string | storage/profile-pictures | Local filesystem path for profile picture uploads when using the local driver. Use a mounted Railway volume in production or switch to s3. |
PROFILE_IMAGE_PUBLIC_BASE_URL | string | (none) | Public URL prefix for serving locally stored profile images. |
Storage — Call Recordings
| Variable | Type | Default | Description |
|---|
CALL_RECORDINGS_STORAGE_DRIVER | string | local | Storage driver for call recording uploads. Accepted: local, s3, railway_bucket, railway. railway_bucket and railway are aliases for s3. |
CALL_RECORDINGS_LOCAL_DIR | string | storage/call-recordings | Local filesystem path for call recording files when using the local driver. Use a mounted Railway volume or switch to s3 for persistent production storage. |
CALL_RECORDINGS_MAX_UPLOAD_MB | integer | 100 | Maximum upload size for a single call recording file, in megabytes. |
S3 / Object Storage
Used for call recordings and profile images when the storage driver is set to s3. Both AWS_* and S3_* prefixes are accepted; AWS_* takes priority.
| Variable | Aliases | Type | Description |
|---|
S3_ENDPOINT_URL | AWS_ENDPOINT_URL | string | S3-compatible endpoint URL, e.g. https://<bucket-id>.r2.cloudflarestorage.com. |
S3_BUCKET | AWS_S3_BUCKET_NAME | string | Bucket name. |
AWS_ACCESS_KEY_ID | S3_ACCESS_KEY_ID | string | S3 access key. |
AWS_SECRET_ACCESS_KEY | S3_SECRET_ACCESS_KEY | string | S3 secret key. |
S3_REGION | AWS_DEFAULT_REGION | string | Region. Defaults to auto when unset (suitable for Cloudflare R2). |
S3_URL_STYLE | AWS_S3_URL_STYLE | string | URL style: virtual or path. Defaults to virtual. |
S3_PUBLIC_BASE_URL | — | string | Optional public base URL for generating direct links to objects in the bucket (e.g. a CDN prefix). When unset, the API constructs URLs from S3_ENDPOINT_URL and S3_BUCKET. |
Push Notifications (Web Push / VAPID)
Web Push is optional. If all three VAPID_* variables are set, the API enables browser push notifications. If any one is missing, delivery stays disabled and a safe warning is logged.
| Variable | Type | Default | Description |
|---|
PUSH_NOTIFICATIONS_ENABLED | boolean | True | Master switch for the push-notification subsystem. Set to False to disable all outbound push delivery without removing VAPID keys. |
EXPO_PUSH_API_URL | string | https://exp.host/--/api/v2/push/send | Expo push gateway URL. Only relevant if the mobile (Capacitor/Expo) push path is active. Change only if self-hosting the Expo push service. |
PUSH_NOTIFICATION_MESSAGE_PREVIEW_ENABLED | boolean | True | When True, notification payloads include a truncated message preview. Set to False to send title-only notifications for privacy. |
VAPID_PUBLIC_KEY | string | (none) | VAPID public key for browser push subscriptions. Generate with npx web-push generate-vapid-keys. |
VAPID_PRIVATE_KEY | string | (none) | VAPID private key. API service only — never set on the Frontend service. |
VAPID_SUBJECT | string | (none) | Contact URI in the VAPID header, e.g. mailto:[email protected]. |
Do not set NEXT_PUBLIC_VAPID_PUBLIC_KEY on the web service. The browser fetches the public key dynamically from GET /api/v1/notifications/push-public-key.
Attendance & Scheduling
| Variable | Type | Default | Description |
|---|
ATTENDANCE_MAX_ACTIVE_HOURS | integer | 16 | Maximum hours a check-in session can remain open before the system auto-closes it. |
ATTENDANCE_AUTO_CLOSE_GRACE_MINUTES | integer | 60 | Grace period (minutes) added after ATTENDANCE_MAX_ACTIVE_HOURS before the auto-close Celery task fires. |
BUSINESS_TIMEZONE | string | Asia/Karachi | IANA timezone identifier used for shift windows, roster generation, and end-of-day processing. Example values: UTC, America/New_York, Europe/London. |