Corejobtrack Server — Deployment Guide
> Audience: System administrators and DevOps engineers responsible for deploying Corejobtrack Server to production.
---
Table of Contents
7. Systemd Service Configuration
8. Firewall & Network Configuration
10. Apple Push Notifications (APNs) Setup
12. Vector Search / AI Setup (Optional)
16. Troubleshooting
---
1. Prerequisites
Before deploying Corejobtrack Server, ensure you have:
| Requirement | Minimum Version | Notes |
| Linux Server | Ubuntu 20.04+ / RHEL 8+ | Deployed on Linux with systemd |
| Python | 3.10+ | 3.11 or 3.12 recommended |
| PostgreSQL | 14+ | 15 or 16 recommended |
| Git | 2.25+ | For deployment via `git pull` |
| OpenSSL | 1.1+ | For SSL certificate generation |
Required Python Packages
The key dependencies include:
Install all dependencies:
pip install -r requirements.txt
---
2. Server Requirements
Hardware Recommendations
| Component | Minimum | Recommended |
| CPU | 2 cores | 4+ cores |
| RAM | 2 GB | 4+ GB |
| Disk | 20 GB | 50+ GB (depends on file uploads) |
| Network | 100 Mbps | 1 Gbps |
Directory Structure
The production deployment typically lives at:
/opt/taskapp/
├── app/ # Application code (git repository)
│ ├── main.py
│ ├── app/
│ ├── uploads/ # File upload storage
│ ├── taskapp.env # Environment configuration (NOT in git)
│ ├── server_cert.pem # SSL certificate
│ ├── server_key.pem # SSL private key
│ └── venv/ # Python virtual environment
├── restart_services.sh # Deployment restart script
└── .deploy_result.json # Last deployment result
---
3. PostgreSQL Database Setup
Install PostgreSQL
# Ubuntu/Debian
sudo apt update
sudo apt install postgresql postgresql-contrib
# RHEL/CentOS
sudo dnf install postgresql-server postgresql-contrib
sudo postgresql-setup --initdb
Create Database and User
sudo -u postgres psql
-- Create the database user
CREATE USER corejobtrack_user WITH PASSWORD 'your_secure_password_here';
-- Create the database
CREATE DATABASE corejobtrack OWNER corejobtrack_user;
-- Grant privileges
GRANT ALL PRIVILEGES ON DATABASE corejobtrack TO corejobtrack_user;
-- Connect to the database to set up extensions
\c corejobtrack
-- Required: enable UUID generation
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Optional: enable vector search (requires pgvector installed)
-- CREATE EXTENSION IF NOT EXISTS vector;
Configure Remote Access (if DB is on a separate server)
Edit pg_hba.conf to allow connections from the application server:
# Allow Corejobtrack server to connect
host corejobtrack corejobtrack_user 10.0.0.0/24 scram-sha-256
Edit postgresql.conf:
listen_addresses = '*' # Or specific IP
port = 5432
Restart PostgreSQL:
sudo systemctl restart postgresql
Schema Auto-Migration
Corejobtrack automatically creates and patches the database schema on startup via ensure_schema(). You do not need to run SQL migration scripts manually. The first startup will create all required tables.
For reference, the full schema is documented in corejobtrack_schema.sql.
---
4. Application Installation
Clone the Repository
sudo mkdir -p /opt/taskapp
cd /opt/taskapp
git clone <your-repo-url> app
cd app
Create Virtual Environment
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
Set Permissions
# Create a dedicated service user (optional but recommended)
sudo useradd -r -s /bin/false taskapp
sudo chown -R taskapp:taskapp /opt/taskapp
# Ensure uploads directory exists with correct permissions
mkdir -p /opt/taskapp/app/uploads
chmod 750 /opt/taskapp/app/uploads
---
5. Environment Configuration
Copy the sample environment file and edit it with your production values:
cp sample.taskapp.env taskapp.env
chmod 600 taskapp.env # Restrict access to owner only
Required Configuration
Edit taskapp.env with your production settings:
# ===================== DATABASE =====================
DB_NAME=corejobtrack
DB_USER=corejobtrack_user
DB_PASSWORD=your_secure_password_here
DB_HOST=localhost # Or remote DB IP (see Guide 9 for breakout)
DB_PORT=5432
# ===================== SECURITY =====================
# CRITICAL: Generate a unique, random secret key for production
# Use: python3 -c "import secrets; print(secrets.token_urlsafe(64))"
SECRET_KEY=your_random_64_char_secret_key_here
# Token expiration (in minutes). Default: 720 (12 hours)
ACCESS_TOKEN_EXPIRE_MINUTES=720
# ===================== UPLOADS =====================
UPLOAD_DIR=/opt/taskapp/app/uploads
MAX_UPLOAD_FILE_SIZE=52428800 # 50 MB
# ===================== COMPANY BRANDING =====================
COMPANY_NAME=Your Company Name
COMPANY_ADDRESS=123 Business Ave, Suite 100, City, ST 12345
COMPANY_PHONE=(555) 123-4567
[email protected]
COMPANY_WEBSITE=www.yourcompany.com
# ===================== PORTAL =====================
PORTAL_BASE_URL=https://tasks.yourcompany.com
Security Configuration
# ===================== RATE LIMITING =====================
RATE_LIMIT_ENABLED=true
RATE_LIMIT_LOGIN_MAX=5 # Max login attempts per window
RATE_LIMIT_LOGIN_WINDOW=300 # 5-minute window
RATE_LIMIT_REGISTER_MAX=3 # Max registrations per window
RATE_LIMIT_REGISTER_WINDOW=3600 # 1-hour window
# ===================== ACCOUNT LOCKOUT =====================
ACCOUNT_LOCKOUT_ENABLED=true
ACCOUNT_LOCKOUT_THRESHOLD=10 # Lockout after 10 failed attempts
ACCOUNT_LOCKOUT_DURATION=900 # 15-minute lockout
# ===================== CORS =====================
# Set specific origins for production (comma-separated)
CORS_ORIGINS=https://tasks.yourcompany.com,https://app.yourcompany.com
CORS_ALLOW_CREDENTIALS=true
# ===================== REQUEST LIMITS =====================
MAX_REQUEST_SIZE_BYTES=10485760 # 10 MB max request body
Email Configuration
# ===================== EMAIL (SMTP) =====================
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
[email protected]
SMTP_PASSWORD=xxxx xxxx xxxx xxxx # Google App Password
SMTP_FROM_NAME=Corejobtrack
[email protected]
SMTP_ENABLED=true
# CRITICAL: Set to false for production!
SMTP_DEV_MODE=false
SMTP_DEV_REDIRECT_ADDRESS=
# Admin email for deployment notifications
[email protected]
> Warning: When SMTP_DEV_MODE=true, ALL outgoing emails are redirected to SMTP_DEV_REDIRECT_ADDRESS instead of the actual recipient. Always set this to false in production.
Connection Pool Configuration
# ===================== DATABASE POOL =====================
DB_POOL_ENABLED=true
DB_POOL_MIN_CONNECTIONS=2
DB_POOL_MAX_CONNECTIONS=20
DB_MAX_RETRIES=5
DB_RETRY_BASE_DELAY=1.0
DB_RETRY_MAX_DELAY=30.0
DB_HEALTH_CHECK_INTERVAL=30
---
6. SSL Certificate Setup
Corejobtrack Server should run with SSL in production.
Option A: Self-Signed Certificate (Internal Use)
If a generate_cert.py script is included:
cd /opt/taskapp/app
source venv/bin/activate
python generate_cert.py
This generates server_cert.pem and server_key.pem in the project root.
Option B: Let's Encrypt (Public-Facing)
sudo apt install certbot
sudo certbot certonly --standalone -d tasks.yourcompany.com
# Copy certificates
sudo cp /etc/letsencrypt/live/tasks.yourcompany.com/fullchain.pem /opt/taskapp/app/server_cert.pem
sudo cp /etc/letsencrypt/live/tasks.yourcompany.com/privkey.pem /opt/taskapp/app/server_key.pem
sudo chown taskapp:taskapp /opt/taskapp/app/server_*.pem
Option C: Commercial Certificate
Place your certificate files at:
---
7. Systemd Service Configuration
Create a systemd service file for the main server:
sudo nano /etc/systemd/system/taskmanager-ssl.service
[Unit]
Description=Corejobtrack Server (SSL)
After=network.target postgresql.service
Wants=postgresql.service
[Service]
Type=simple
User=taskapp
Group=taskapp
WorkingDirectory=/opt/taskapp/app
Environment=PATH=/opt/taskapp/app/venv/bin:/usr/bin
EnvironmentFile=/opt/taskapp/app/taskapp.env
ExecStart=/opt/taskapp/app/venv/bin/uvicorn main:app \
--host 0.0.0.0 \
--port 8443 \
--ssl-keyfile=server_key.pem \
--ssl-certfile=server_cert.pem \
--workers 4
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable taskmanager-ssl
sudo systemctl start taskmanager-ssl
Check Status
sudo systemctl status taskmanager-ssl
sudo journalctl -u taskmanager-ssl -f # Follow logs
---
8. Firewall & Network Configuration
Open Required Ports
# UFW (Ubuntu)
sudo ufw allow 8443/tcp # Corejobtrack API + Portal
# firewalld (RHEL/CentOS)
sudo firewall-cmd --permanent --add-port=8443/tcp
sudo firewall-cmd --reload
Reverse Proxy with Nginx (Optional)
If you want to serve on standard port 443:
server {
listen 443 ssl;
server_name tasks.yourcompany.com;
ssl_certificate /etc/letsencrypt/live/tasks.yourcompany.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/tasks.yourcompany.com/privkey.pem;
client_max_body_size 50M;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
When using a reverse proxy, run Uvicorn without SSL flags:
ExecStart=/opt/taskapp/app/venv/bin/uvicorn main:app \
--host 127.0.0.1 \
--port 8000 \
--workers 4
---
9. Email (SMTP) Configuration
Corejobtrack uses email for:
Google Workspace Setup
1. Create a service account email (e.g., [email protected]) in Google Admin Console
2. Enable 2-Step Verification on the account
3. Generate an App Password:
Verify Email Configuration
After starting the server, check email status via the API:
curl -s https://tasks.yourcompany.com:8443/email/status \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN" | jq
---
10. Apple Push Notifications (APNs) Setup
Required for push notifications to iOS/macOS clients.
Apple Developer Account Setup
1. Log into Apple Developer Portal
2. Go to Certificates, Identifiers & Profiles > Keys
3. Create a new key with "Apple Push Notifications service (APNs)" enabled
4. Download the .p8 file and note the Key ID
5. Note your Team ID from Membership details
Configuration
APNS_KEY_ID=ABC1234567 # 10-character Key ID
APNS_TEAM_ID=DEF1234567 # 10-character Team ID
APNS_BUNDLE_ID=com.yourcompany.corejobtrack
APNS_KEY_PATH=/opt/taskapp/app/AuthKey_ABC1234567.p8
APNS_USE_SANDBOX=false # Set to false for production
Place the .p8 key file at the path specified in APNS_KEY_PATH.
> Note: APNs configuration can also be managed through the admin API endpoints (/admin/apns/config). See the Admin Portal Guide for details.
---
11. File Upload Storage
Corejobtrack stores file attachments, profile pictures, and signature images on the local filesystem.
UPLOAD_DIR=/opt/taskapp/app/uploads
MAX_UPLOAD_FILE_SIZE=52428800 # 50 MB per file
Allowed File Extensions
The default allowed extensions cover common business documents, images, videos, and archives:
pdf, doc, docx, xls, xlsx, csv, txt, rtf, png, jpg, jpeg, gif, bmp,
tiff, tif, webp, svg, heic, mp4, mov, avi, mp3, wav, zip, rar, 7z,
ppt, pptx, pages, numbers, keynote
Backup Strategy
Include the uploads/ directory in your backup plan. These files are referenced by database records and cannot be regenerated.
---
12. Vector Search / AI Setup (Optional)
Corejobtrack supports natural language search powered by OpenAI embeddings and PostgreSQL pgvector.
Install pgvector Extension
# Ubuntu/Debian
sudo apt install postgresql-16-pgvector
# Or compile from source
cd /tmp
git clone https://github.com/pgvector/pgvector.git
cd pgvector
make
sudo make install
Enable in your database:
\c corejobtrack
CREATE EXTENSION IF NOT EXISTS vector;
Configure Embeddings
EMBEDDING_ENABLED=true
EMBEDDING_API_KEY=sk-your-openai-api-key
EMBEDDING_MODEL=text-embedding-3-small
EMBEDDING_DIMENSIONS=1536
EMBEDDING_BATCH_SIZE=100
EMBEDDING_MAX_TOKENS=8000
When enabled, Corejobtrack will:
> Note: This feature requires an OpenAI API key and incurs usage costs. You can manage the budget cap through the admin API.
---
13. SMB Hot Folder (Optional)
Corejobtrack can monitor an SMB network share for JSON files and automatically create tasks/tickets from them.
Requirements
Configuration
SMB watch is configured through the admin API (/admin/smb-watch/config) rather than environment variables. See the Admin Portal Guide for setup instructions.
---
14. Production Checklist
Before going live, verify these items:
---
15. Upgrading & Redeployment
Method 1: Admin API (Recommended)
Admin users can trigger a redeployment from the client application:
curl -X POST https://tasks.yourcompany.com:8443/admin/restart \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN"
This executes restart_services.sh, which:
1. Performs git pull to fetch latest code
2. Installs any new dependencies
3. Restarts the systemd service
4. Saves deployment results to .deploy_result.json
5. Sends a deployment report email to ADMIN_EMAIL
Method 2: Manual Deployment
cd /opt/taskapp/app
git pull origin main
source venv/bin/activate
pip install -r requirements.txt
sudo systemctl restart taskmanager-ssl
Check Deployment Status
curl -s https://tasks.yourcompany.com:8443/admin/deploy-status \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN" | jq
---
16. Troubleshooting
Server Won't Start
# Check service status and logs
sudo systemctl status taskmanager-ssl
sudo journalctl -u taskmanager-ssl --no-pager -n 50
# Test manually
cd /opt/taskapp/app
source venv/bin/activate
uvicorn main:app --host 0.0.0.0 --port 8443 \
--ssl-keyfile=server_key.pem --ssl-certfile=server_cert.pem
Database Connection Failures
# Test connectivity
psql -h DB_HOST -U DB_USER -d corejobtrack -c "SELECT 1;"
# Check DB status via API
curl -s https://tasks.yourcompany.com:8443/dbhealth
curl -s https://tasks.yourcompany.com:8443/db-status
SSL Certificate Issues
Check certificate via the admin API:
curl -s https://tasks.yourcompany.com:8443/admin/ssl/cert-info \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN" | jq
Or renew via API:
curl -X POST https://tasks.yourcompany.com:8443/admin/ssl/renew-cert \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN"
Log File Locations
Corejobtrack uses rotating log files stored in the application directory. Access logs via the admin API:
# Get last 100 lines
curl -s "https://tasks.yourcompany.com:8443/logs?lines=100" \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN"
# Export logs for a date range
curl -s "https://tasks.yourcompany.com:8443/logs/export?date_from=2026-01-01&date_to=2026-01-31" \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN"
---
Next: Guide 2 — Onboarding Wizard | Guide 3 — Launch & Verify
Corejobtrack Server — Onboarding Wizard
> Audience: First-time administrators setting up Corejobtrack for their organization. This guide walks through the initial setup process step by step.
---
Table of Contents
1. Overview
3. Step 2: Database Initialization
4. Step 3: Create the First Admin User
5. Step 4: Company Branding Configuration
6. Step 5: Configure Email Service
7. Step 6: Set Up the Customer Portal
8. Step 7: Configure Default Settings
9. Step 8: Create Employee Accounts
10. Step 9: Create Your First Customer
11. Step 10: Verify Everything Works
12. What's Next?
---
1. Overview
This guide walks you through setting up Corejobtrack from a freshly deployed server to a fully configured, ready-to-use system. By the end, you'll have:
!Placeholder: Onboarding flow diagram
Before You Begin
Make sure you've completed the Deployment Guide. You should have:
---
2. Step 1: Environment Setup
Copy and Edit the Environment File
cd /opt/taskapp/app # Or your installation directory
cp sample.taskapp.env taskapp.env
Open taskapp.env in your preferred editor. At minimum, configure these fields:
# Database connection
DB_NAME=corejobtrack
DB_USER=your_db_user
DB_PASSWORD=your_db_password
DB_HOST=localhost
DB_PORT=5432
# Security — GENERATE A UNIQUE KEY!
# Run: python3 -c "import secrets; print(secrets.token_urlsafe(64))"
SECRET_KEY=paste_your_generated_key_here
Generate a Secure Secret Key
python3 -c "import secrets; print(secrets.token_urlsafe(64))"
Copy the output and paste it as your SECRET_KEY value.
> Important: Never share your SECRET_KEY or commit it to version control. Each deployment should have its own unique key.
---
3. Step 2: Database Initialization
Start the Server for the First Time
The database schema is created automatically on first startup:
source venv/bin/activate
uvicorn main:app --host 0.0.0.0 --port 8443 \
--ssl-keyfile=server_key.pem --ssl-certfile=server_cert.pem
Watch the console output for:
INFO: [DB] Schema migration complete
INFO: [DB] Created 'hotfolder' system user for SMB ingest
INFO: Uvicorn running on https://0.0.0.0:8443
Verify Database Connection
Open a new terminal and test:
curl -k https://localhost:8443/health
# Expected: {"ok": true}
curl -k https://localhost:8443/dbhealth
# Expected: {"db": "ok", "database": "corejobtrack", "server": "PostgreSQL 16.x"}
!Placeholder: Health check response screenshot
---
4. Step 3: Create the First Admin User
Register Your Admin Account
Send a registration request to create the first user:
curl -k -X POST https://localhost:8443/register \
-H "Content-Type: application/json" \
-d '{
"username": "admin",
"password": "YourSecurePassword123!",
"full_name": "System Administrator",
"first_name": "System",
"last_name": "Administrator",
"email": "[email protected]",
"phone": "(555) 123-4567"
}'
Promote to Admin
New registrations start with role: "pending". You need to promote the first user directly in the database:
sudo -u postgres psql corejobtrack
-- View the registered user
SELECT id, username, role, is_admin FROM users;
-- Promote to admin
UPDATE users SET role = 'admin', is_admin = TRUE WHERE username = 'admin';
Verify Admin Login
curl -k -X POST https://localhost:8443/token \
-d "username=admin&password=YourSecurePassword123!" \
-H "Content-Type: application/x-www-form-urlencoded"
You should receive an access token:
{
"access_token": "eyJ...",
"token_type": "bearer"
}
Save this token — you'll use it for the remaining setup steps.
# Set as an environment variable for convenience
export TOKEN="eyJ..."
---
5. Step 4: Company Branding Configuration
Company branding appears in PDF reports, email templates, and the customer portal.
Configure in `taskapp.env`
COMPANY_NAME=Acme Corporation
COMPANY_ADDRESS=123 Main Street, Anytown, ST 12345
COMPANY_PHONE=(555) 123-4567
[email protected]
COMPANY_WEBSITE=www.acme.com
Restart to Apply
sudo systemctl restart taskmanager-ssl
The branding is used in:
!Placeholder: PDF with company branding
---
6. Step 5: Configure Email Service
Email is required for the customer portal (magic link login) and signature requests.
Google Workspace / Gmail Setup
1. Create a service account — Use an email like [email protected]
2. Enable 2-Step Verification — Required for App Passwords
3. Generate an App Password:
Update `taskapp.env`
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
[email protected]
SMTP_PASSWORD=xxxx xxxx xxxx xxxx # Your App Password
SMTP_FROM_NAME=Corejobtrack
[email protected]
SMTP_ENABLED=true
# FOR TESTING: Keep true to redirect all emails to yourself
SMTP_DEV_MODE=true
[email protected]
# FOR PRODUCTION: Set to false when ready
# SMTP_DEV_MODE=false
Test Email Configuration
After restarting the server:
curl -k -X POST https://localhost:8443/email/send \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"to": "[email protected]",
"subject": "Corejobtrack Email Test",
"body_text": "If you received this email, email is configured correctly!",
"body_html": "<h1>Success!</h1><p>Corejobtrack email is working.</p>"
}'
Check Email Status
curl -k https://localhost:8443/email/status \
-H "Authorization: Bearer $TOKEN"
> Tip: Start with SMTP_DEV_MODE=true during setup. This redirects ALL outgoing emails to your SMTP_DEV_REDIRECT_ADDRESS, so you can verify emails are formatted correctly without sending to real customers.
---
7. Step 6: Set Up the Customer Portal
The customer portal allows your customers to view their tickets, communicate with your team, and sign service documents online.
Configure Portal URL
In taskapp.env:
PORTAL_BASE_URL=https://tasks.yourcompany.com:8443
# Session settings
PORTAL_MAGIC_LINK_EXPIRY_MINUTES=15
PORTAL_SESSION_EXPIRY_DAYS=7
PORTAL_REMEMBER_ME_DAYS=30
Verify Portal Access
Open a web browser and navigate to:
!Placeholder: Customer portal login page
The customer portal uses magic link authentication — customers enter their email, receive a link, and click to log in. No password is required for portal access.
---
8. Step 7: Configure Default Settings
Set Ticket Color Defaults
Configure the default colors for new service and support tickets:
curl -k -X PUT https://localhost:8443/settings/ticket-defaults \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"service_ticket": "#4A90D9",
"support_ticket": "#E67E22"
}'
Set Up Dropdown Choices
Configure the dropdown options available when creating tickets. Common fields include:
# Task types
curl -k -X POST https://localhost:8443/task_types/ \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Installation"}'
curl -k -X POST https://localhost:8443/task_types/ \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Repair"}'
curl -k -X POST https://localhost:8443/task_types/ \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Maintenance"}'
Set Up Dropdown Choices for Custom Fields
# Dispatch types for service tickets
curl -k -X PUT https://localhost:8443/settings/dropdown-choices/dispatch_type \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '[
{"value": "Emergency", "label": "Emergency"},
{"value": "Scheduled", "label": "Scheduled"},
{"value": "Preventive Maintenance", "label": "Preventive Maintenance"}
]'
---
9. Step 8: Create Employee Accounts
Method 1: Admin Creates Users Directly
curl -k -X POST https://localhost:8443/users/ \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"username": "jsmith",
"password": "TempPassword123!",
"full_name": "John Smith",
"first_name": "John",
"last_name": "Smith",
"email": "[email protected]",
"phone": "(555) 234-5678",
"team": "Service"
}'
Method 2: Employee Self-Registration
Employees can register at POST /register. Their accounts start with role: "pending" and must be approved by an admin:
# Approve a pending user
curl -k -X PUT https://localhost:8443/users/USER_ID \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"role": "user"}'
User Roles
| Role | Access Level | Description |
| `pending` | None | Newly registered, awaiting approval |
| `user` | Standard | Can create/manage tasks and tickets |
| `admin` | Full | All user permissions + admin panel access |
Teams
Teams are text strings that can be assigned to users and used for ticket visibility:
---
10. Step 9: Create Your First Customer
Create a Customer
curl -k -X POST https://localhost:8443/customers/ \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"company": "Customer Corp",
"contact_id": "CUST001",
"address_1": "456 Customer Ave",
"city": "Townsville",
"state": "ST",
"zip": "67890",
"account_status": "active"
}'
Add a Contact
curl -k -X POST https://localhost:8443/customers/CUSTOMER_ID/contacts \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"first_name": "Jane",
"last_name": "Doe",
"phone": "(555) 345-6789",
"email": "[email protected]",
"is_primary": true
}'
Create a Test Ticket
curl -k -X POST https://localhost:8443/service-tickets/ \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"customer_name": "Customer Corp",
"title": "Initial Setup Verification",
"description": "Test ticket to verify system is working correctly",
"urgency": "Low",
"status": "Pending"
}'
---
11. Step 10: Verify Everything Works
Verification Checklist
Run through this checklist to confirm your setup is complete:
| Test | Command / Action | Expected Result |
| Server Health | `curl -k https://localhost:8443/health` | `{"ok": true}` |
| Database Health | `curl -k https://localhost:8443/dbhealth` | `{"db": "ok", ...}` |
| Server Version | `curl -k https://localhost:8443/version` | Version number |
| Admin Login | `POST /token` with admin credentials | Access token returned |
| List Users | `GET /users/` with admin token | User list with your admin |
| List Customers | `GET /customers/list` with token | Your test customer |
| List Tickets | `GET /service-tickets/` with token | Your test ticket |
| Portal Login Page | Open browser to `/customer/login` | Login form renders |
| Admin Portal | Open browser to `/portal/admin/login` | Admin login form |
| Email Test | `POST /email/send` | Email received |
Common Issues
| Problem | Solution |
| `{"detail": "Not authenticated"}` | Include `Authorization: Bearer TOKEN` header |
| `{"detail": "Not authorized"}` | User needs admin role for this endpoint |
| Portal returns 404 | Check `PORTAL_BASE_URL` is correct |
| Email not received | Check `SMTP_ENABLED=true` and verify credentials |
| Database connection timeout | Verify `DB_HOST`, `DB_PORT`, and `pg_hba.conf` |
---
12. What's Next?
Congratulations! Your Corejobtrack Server is set up and ready. Here's what to explore next:
| Guide | What You'll Learn |
| Launch & Verify | Detailed server startup, health monitoring, and diagnostics |
| Customer Portal | How customers interact with the portal |
| Employee Guide | Day-to-day employee workflows |
| Admin Guide | System administration and management |
| API Reference | Complete API endpoint documentation |
| JSON Inputs | Request/response format reference |
| Database Breakout | Moving the database to a separate server |
---
Previous: Guide 1 — Deployment | Next: Guide 3 — Launch & Verify
Corejobtrack Server — Launch & Verify Guide
> Audience: Administrators and developers who need to start the server, verify it's running correctly, and diagnose issues.
---
Table of Contents
---
1. Starting the Server
Development Mode
For local development with auto-reload:
cd /path/to/corejobtrack-server
source venv/bin/activate
# Load environment variables
export $(grep -v '^#' taskapp.env | xargs)
# Start with auto-reload (restarts on code changes)
uvicorn main:app --reload --host 0.0.0.0 --port 8000
!Placeholder: Terminal showing uvicorn startup output
Production Mode (SSL)
uvicorn main:app --host 0.0.0.0 --port 8443 \
--ssl-keyfile=server_key.pem \
--ssl-certfile=server_cert.pem \
--workers 4
Production Mode (Systemd)
# Start the service
sudo systemctl start taskmanager-ssl
# Enable auto-start on boot
sudo systemctl enable taskmanager-ssl
# Check status
sudo systemctl status taskmanager-ssl
What Happens at Startup
When the server starts, it performs these operations in sequence:
1. Load configuration from taskapp.env
2. Create FastAPI application
3. Register middleware (CORS, security headers, request limits, logging)
4. Register all route modules (38 modules)
5. Run ensure_schema() — create/migrate database tables
6. Start DB health monitor thread
7. Start SMB watch thread (if configured)
8. Start signature reminder scheduler
9. Send deployment report email (if server was restarted via admin)
10. Initialize embedding/vector search (if configured)
11. Begin accepting HTTP requests
---
2. Health Check Endpoints
Corejobtrack provides several health check endpoints that require no authentication. Use these to verify the server is running.
Basic Health Check
curl https://your-server:8443/health
Response:
{"ok": true}
Use this endpoint for load balancer health probes and uptime monitoring.
Server Version
curl https://your-server:8443/version
Response:
{"version": "0.981"}
Useful for confirming which version is deployed after an update.
Database Health
curl https://your-server:8443/dbhealth
Response (healthy):
{
"db": "ok",
"database": "corejobtrack",
"server": "PostgreSQL 16.1 on x86_64-pc-linux-gnu"
}
Response (unhealthy):
{
"detail": "Database unavailable"
}
(HTTP 503)
Detailed Database Status
curl https://your-server:8443/db-status
Response:
{
"available": true,
"last_check": "2026-03-01T12:00:00Z",
"last_error": null,
"last_recovery": null,
"consecutive_failures": 0,
"total_recoveries": 0
}
This endpoint provides detailed information about database connectivity, including:
| Field | Description |
| `available` | Whether the database is currently reachable |
| `last_check` | Timestamp of the last health check |
| `last_error` | Most recent error message (null if healthy) |
| `last_recovery` | When the database last recovered from a failure |
| `consecutive_failures` | Number of consecutive failed checks |
| `total_recoveries` | Total number of times the DB recovered |
Connection Pool Statistics (Admin Only)
curl https://your-server:8443/db-pool-stats \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN"
Response:
{
"available": 5,
"in_use": 2,
"max": 20,
"min": 2
}
Security Status (Admin Only)
curl https://your-server:8443/security-status \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN"
Returns the current security configuration including rate limiting, CORS, and lockout settings.
---
3. Startup Sequence
Successful Startup Log
A healthy startup produces log output similar to:
INFO: [DB] Schema migration complete
INFO: [DB] Created 'hotfolder' system user for SMB ingest
INFO: [DB] Health monitor started (interval: 30s)
INFO: [SMB] Watcher did not start: SMB not configured
INFO: [SIGNATURE] Reminder scheduler started
INFO: [EMBED] pgvector not installed — vector search disabled, ILIKE fallback active
INFO: Uvicorn running on https://0.0.0.0:8443 (Press CTRL+C to quit)
Schema Migration
On first startup (or when updates add new columns), you'll see migration messages:
INFO: [DB] Added column 'change_log' to tasks
INFO: [DB] Added column 'task_type' to tasks
INFO: [DB] Added column 'color' to tasks
INFO: [DB] Created system user 'hotfolder'
INFO: [DB] Created system user 'portal'
INFO: [DB] Schema migration complete
> Note: Schema migration is automatic and non-destructive. It only adds new columns — it never drops or modifies existing data.
---
4. Verifying Core Services
After startup, run these checks to verify all services are operational.
Quick Verification Script
#!/bin/bash
SERVER="https://localhost:8443"
TOKEN="YOUR_ADMIN_TOKEN"
echo "=== Corejobtrack Server Verification ==="
# 1. Health
echo -n "Health Check: "
curl -sk $SERVER/health | jq -r '.ok'
# 2. Version
echo -n "Version: "
curl -sk $SERVER/version | jq -r '.version'
# 3. Database
echo -n "Database: "
curl -sk $SERVER/dbhealth | jq -r '.db'
# 4. DB Status
echo -n "DB Available: "
curl -sk $SERVER/db-status | jq -r '.available'
# 5. Pool Stats
echo -n "Pool Connections: "
curl -sk $SERVER/db-pool-stats -H "Authorization: Bearer $TOKEN" | jq -r '"available=\(.available), in_use=\(.in_use)"'
# 6. Auth Test
echo -n "Auth Working: "
curl -sk $SERVER/me -H "Authorization: Bearer $TOKEN" | jq -r '.username // "FAILED"'
# 7. Email Status
echo -n "Email Configured: "
curl -sk $SERVER/email/status -H "Authorization: Bearer $TOKEN" | jq -r '.configured // "false"'
echo "=== Verification Complete ==="
Service-by-Service Verification
#### Authentication Service
# Login and get a token
curl -sk -X POST https://localhost:8443/token \
-d "username=admin&password=YourPassword" \
-H "Content-Type: application/x-www-form-urlencoded"
# Verify the token works
curl -sk https://localhost:8443/me \
-H "Authorization: Bearer YOUR_TOKEN"
#### Database Connection
# Basic connectivity
curl -sk https://localhost:8443/dbhealth
# Detailed status with recovery tracking
curl -sk https://localhost:8443/db-status
# Schema check (admin only)
curl -sk https://localhost:8443/admin/db/schema-status \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN"
#### Email Service
curl -sk https://localhost:8443/email/status \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN"
#### Push Notifications (APNs)
curl -sk https://localhost:8443/admin/apns/status \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN"
#### SSL Certificate
curl -sk https://localhost:8443/admin/ssl/cert-info \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN"
#### SMB Hot Folder
curl -sk https://localhost:8443/admin/smb-watch/status \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN"
#### Embedding / Vector Search
curl -sk https://localhost:8443/admin/embedding/status \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN"
---
5. Monitoring & Diagnostics
Continuous Monitoring
For production environments, set up monitoring using the health endpoints:
| Endpoint | Interval | Alert On |
| `GET /health` | 30 seconds | Non-200 response |
| `GET /dbhealth` | 60 seconds | Non-200 response |
| `GET /db-status` | 5 minutes | `consecutive_failures > 3` |
| `GET /version` | After deployments | Version mismatch |
Systemd Journal Logs
# Follow live logs
sudo journalctl -u taskmanager-ssl -f
# Last 100 lines
sudo journalctl -u taskmanager-ssl --no-pager -n 100
# Logs since specific time
sudo journalctl -u taskmanager-ssl --since "2026-03-01 08:00:00"
# Filter by priority
sudo journalctl -u taskmanager-ssl -p err # Only errors
Application Logs (Admin API)
# Get last 200 lines of server log
curl -sk "https://localhost:8443/logs?lines=200" \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN"
# Export logs for a date range
curl -sk "https://localhost:8443/logs/export?date_from=2026-02-28&date_to=2026-03-01" \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN"
Database Schema Diagnostics
Check which optional columns exist and get SQL to add missing ones:
curl -sk https://localhost:8443/admin/db/schema-status \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN"
Response example:
{
"tasks": {
"change_log": true,
"task_type": true,
"is_private": true,
"archived_at": true,
"color": true
},
"service_tickets": {
"change_log": true
},
"task_steps": {
"completed_at": true,
"completed_by": true
},
"missing_sql": []
}
If missing_sql is not empty, run the provided SQL statements to bring the schema up to date, or simply restart the server to trigger auto-migration.
---
6. Common Startup Issues
Issue: "Database unavailable" on startup
Symptoms: Server starts but returns 503 on database-related requests.
Diagnosis:
# Check if PostgreSQL is running
sudo systemctl status postgresql
# Test database connection directly
psql -h DB_HOST -U DB_USER -d corejobtrack -c "SELECT 1;"
# Check db-status endpoint
curl -sk https://localhost:8443/db-status
Solutions:
Issue: "SECRET_KEY must be set in production"
Symptoms: Server crashes immediately on startup.
Solution: Set the SECRET_KEY environment variable:
python3 -c "import secrets; print(secrets.token_urlsafe(64))"
# Add the output to taskapp.env as SECRET_KEY=...
Issue: SSL certificate not found
Symptoms: Uvicorn errors about missing SSL files.
Solution: Ensure server_cert.pem and server_key.pem exist in the working directory:
ls -la server_cert.pem server_key.pem
Issue: Port already in use
Symptoms: OSError: [Errno 98] Address already in use
Solution:
# Find what's using the port
sudo lsof -i :8443
# Kill the process if needed, or use a different port
Issue: Permission denied on uploads directory
Symptoms: File uploads fail with 500 errors.
Solution:
mkdir -p uploads
chmod 750 uploads
chown taskapp:taskapp uploads
---
7. Stopping & Restarting
Graceful Shutdown
# Systemd
sudo systemctl stop taskmanager-ssl
# Manual (Ctrl+C in terminal, or send SIGTERM)
kill -TERM $(pgrep -f "uvicorn main:app")
On shutdown, the server:
1. Stops the signature scheduler
2. Stops the DB health monitor
3. Stops the embedding backfill (if running)
4. Closes all database connections and shuts down the pool
Restart
# Systemd restart
sudo systemctl restart taskmanager-ssl
# Verify after restart
curl -sk https://localhost:8443/health
curl -sk https://localhost:8443/version
Remote Restart via Admin API
Admin users can trigger a full deployment restart remotely:
curl -sk -X POST https://localhost:8443/admin/restart \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN"
This runs restart_services.sh which:
1. Pulls latest code from git
2. Installs dependencies
3. Restarts systemd services
4. Emails a deployment report
Check the result after the server comes back up:
curl -sk https://localhost:8443/admin/deploy-status \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN"
---
8. Log Files & Rotation
Log Configuration
| Setting | Default | Description |
| `SERVER_LOG_RETENTION_WEEKS` | 52 | How many weekly log files to keep |
| `CLIENT_LOG_RETENTION_WEEKS` | 52 | How many weekly client log files to keep |
Corejobtrack uses Python's TimedRotatingFileHandler with weekly rotation. Old log files are automatically deleted based on the retention setting.
Log Categories
Each log line is prefixed with a category for easy filtering:
| Prefix | Category | Example |
| `[AUTH]` | Authentication | Login, token validation, registration |
| `[DB]` | Database | Connection issues, query failures |
| `[EMAIL]` | Email service | Send success/failure, SMTP errors |
| `[PORTAL]` | Customer/admin portal | Page loads, form submissions |
| `[ADMIN]` | Server administration | Restart, deploy, config changes |
| `[SMB]` | SMB hot folder | File processing, connection issues |
| `[APNS]` | Push notifications | Send success/failure |
| `[UPLOAD]` | File uploads | Upload/download operations |
| `[SIGNATURE]` | Signature system | Request, capture, reminder |
| `[EMBED]` | Vector search | Embedding generation, backfill |
Viewing Logs
# Via admin API
curl -sk "https://localhost:8443/logs?lines=50" \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN"
# Via admin portal
# Navigate to Admin Area > Server Logs
# Via systemd journal
sudo journalctl -u taskmanager-ssl -f
# Export for analysis
curl -sk "https://localhost:8443/logs/export?log_type=server&date_from=2026-02-01&date_to=2026-03-01" \
-H "Authorization: Bearer YOUR_ADMIN_TOKEN" > server_logs.txt
Client Logs
Client applications can submit logs to the server for centralized tracking:
# Single log entry
curl -sk -X POST https://localhost:8443/logs \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"message": "App launched successfully",
"level": "info",
"platform": "ios",
"client_version": "2.1.0"
}'
# Batch submission
curl -sk -X POST https://localhost:8443/logs/batch \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"entries": [
{"message": "App launched", "level": "info"},
{"message": "Network timeout", "level": "warning"}
],
"platform": "ios",
"client_version": "2.1.0"
}'
---
Previous: Guide 2 — Onboarding Wizard | Next: Guide 4 — Customer Portal
Corejobtrack Server — Customer Portal Guide
> Audience: End-user customers who access the Corejobtrack portal to view tickets, communicate with your team, and sign service documents. This guide assumes no technical background.
---
Table of Contents
1. What is the Customer Portal?
2. Getting Started — Registration
3. Logging In
6. Creating a New Service Ticket
7. Creating a New Support Ticket
9. Communicating with the Team
11. Notifications
12. Account Settings
13. Downloading Attachments & PDFs
14. Frequently Asked Questions
---
1. What is the Customer Portal?
The Customer Portal is your self-service window into Corejobtrack. Through the portal, you can:
!Placeholder: Customer portal dashboard overview
Accessing the Portal
Open your web browser and go to the URL provided by your service provider. It typically looks like:
https://tasks.yourcompany.com/customer/login
The portal works on desktop computers, tablets, and phones.
---
2. Getting Started — Registration
If you don't have an account yet, you can register directly through the portal.
Step 1: Navigate to Registration
From the login page, click the "Register" link.
Step 2: Enter Your Customer Number
Enter the customer number or company name associated with your account. This links your portal account to your company's records.
!Placeholder: Registration step 1 - customer number
Step 3: Enter Your Information
Fill in your personal details:
!Placeholder: Registration step 2 - personal info
Step 4: Wait for Approval
After registering, your account will be in a pending state. An administrator will review and approve your registration — typically within 24 hours. You'll receive an email when your account is approved.
!Placeholder: Registration pending screen
---
3. Logging In
Option A: Email & Password
1. Go to the login page
2. Enter your email address and password
3. Check "Remember me" to stay logged in for up to 30 days
4. Click "Sign In"
Option B: Magic Link (No Password Needed)
If you prefer not to use a password, or if you've forgotten it:
1. On the login page, click "Sign in with Magic Link"
2. Enter your email address
3. Click "Send Magic Link"
4. Check your email inbox for a message from Corejobtrack
5. Click the link in the email — you'll be logged in automatically
> Note: Magic links expire after 15 minutes. If it expires, request a new one.
Forgot Your Password?
1. From the login page, click "Forgot Password?"
2. Enter your email address
3. Check your email for a password reset link
4. Click the link and enter a new password
---
4. Your Dashboard
After logging in, you'll see your dashboard — the main hub for all your activity.
!Placeholder: Customer dashboard with tickets
What You'll See
The dashboard shows two tables:
#### Service Tickets
These represent on-site service visits. Each row shows:
| Column | What It Means |
| **Ticket #** | Unique ticket number (e.g., #456789) — click to view details |
| **Title** | Brief description of the service request |
| **Status** | Current state: Open, In Progress, Completed, Archived |
| **Signature** | Whether you've signed for the completed service |
| **Created** | When the ticket was created |
#### Support Tickets
These represent remote support cases (phone, email, or remote desktop). Each row shows:
| Column | What It Means |
| **Ticket #** | Unique ticket number — click to view details |
| **Title** | Brief description of the support request |
| **Status** | Current state: Open, In Progress, Completed, Archived |
| **Created** | When the ticket was created |
Status Badges
| Status | Color | Meaning |
| Open | Blue | New ticket, not yet started |
| In Progress | Yellow | Being actively worked on |
| Completed | Green | Work is finished |
| Archived | Gray | Closed and stored for records |
| Pending | Orange | Waiting for action |
Creating New Tickets
Click the "New Ticket" button at the top of the dashboard. You'll be asked to choose between:
---
5. Viewing Tickets
Click any ticket number or row on the dashboard to view the full details.
Ticket Detail Page
!Placeholder: Ticket detail view
The ticket detail page shows:
#### Header Section
#### Customer & Contact Information
#### Equipment (Service Tickets)
A table of equipment involved in this service visit:
#### Problem Description (Service Tickets)
A detailed description of the issue being addressed.
#### Time Entries (Service Tickets)
A breakdown of the time spent on the service visit:
#### Attachments
#### Signature Section (Service Tickets)
If the service has been signed:
If a signature is still needed, you may see a "Sign Now" option.
#### Notes & Communication (Support Tickets)
A conversation thread where you can exchange messages with the support team.
---
6. Creating a New Service Ticket
Service tickets are for requesting an on-site technician visit.
Step 1: Select Equipment
Choose the equipment that needs service from your equipment inventory. For each piece selected:
1. Check the box next to the equipment
2. Describe the problem in the text field that appears
3. Set the count if multiple units are affected
You can also add new equipment if it's not in your inventory yet using the "Add Equipment" form.
!Placeholder: Equipment selection step
Step 2: Add Details
Step 3: Review & Submit
Review all the information you've entered, then click "Submit Ticket".
After submission, you'll be taken back to your dashboard where the new ticket will appear with a "Pending" status.
---
7. Creating a New Support Ticket
Support tickets are for remote assistance — phone support, email support, or remote desktop sessions.
Step 1: Describe the Issue
Step 2: Review & Submit
Confirm the details and click "Submit Ticket".
---
8. Signing Service Documents
After a technician completes a service visit, you may be asked to sign digitally.
Signing In Person (On-Site)
The technician may present a signature screen on their device. Simply:
1. Review the service summary
2. Write your signature with your finger or stylus
3. Enter your printed name
4. Confirm
Signing Online (Post-Visit)
If you weren't available during the visit, you'll receive a signature request email:
1. Open the email from Corejobtrack
2. Click the "Sign Now" button
3. You'll be taken to a secure signature page
4. Review the service summary, equipment, and work performed
5. Draw your signature on the canvas
!Placeholder: Signature capture screen
6. Enter your full name
7. Check the safety covers checkbox if applicable
8. Click "Submit Signature"
> Note: Signature links expire after 7 days. If your link has expired, contact the service provider to send a new one.
Declining a Signature
If you need to decline signing (e.g., the work wasn't satisfactory):
---
9. Communicating with the Team
On support tickets, you can send and receive messages directly through the ticket detail page.
Sending a Message
1. Open the support ticket
2. Scroll to the Communication Log section
3. Type your message in the text box
4. Click "Send"
!Placeholder: Communication log with messages
What You'll See
Each message shows:
Typing Indicators
When someone on the team is typing a response, you'll see a "typing..." indicator so you know a reply is coming.
Read Receipts
The system tracks when messages are read by team members, so there's transparency about communication.
---
10. Managing Your Equipment
Your equipment inventory is linked to your customer account. Equipment appears in your service tickets.
Viewing Equipment
Equipment associated with a ticket is displayed in the Equipment section of the ticket detail page.
Equipment Details
Click on an equipment item to see:
---
11. Notifications
Corejobtrack sends you notifications when important things happen.
Types of Notifications
| Notification | When It's Sent |
| Ticket Created | A new ticket is created for your account |
| Status Changed | Your ticket's status changes (e.g., to "In Progress") |
| Message Received | A team member replies to your support ticket |
| Signature Requested | A service visit needs your signature |
| Account Approved | Your registration has been approved |
Viewing Notifications
Click the bell icon in the top navigation bar to see your latest notifications. Click "View All" to see the full notifications page.
!Placeholder: Notification dropdown
Managing Notifications
Push Notifications (Mobile App)
If you use the Corejobtrack mobile app, you can also receive push notifications on your phone. These are managed through the app's settings.
---
12. Account Settings
Access your account settings by clicking the gear icon or "Settings" link in the top navigation.
!Placeholder: Account settings page
Profile Picture
Personal Information
Update your name and contact details:
Password Management
To change your password:
1. Enter your current password
2. Enter your new password
3. Confirm the new password
4. Click "Change Password"
Password requirements:
Email Verification
If your email hasn't been verified:
Account Deletion
If you need to delete your account:
1. Click "Request Delete Account"
2. Read the warning about data loss
3. Confirm your request
4. An administrator will process the deletion
> Warning: Account deletion is permanent. All your data, including ticket history, will be removed.
---
13. Downloading Attachments & PDFs
PDF Summaries
Generate a professional PDF summary of any ticket:
1. Open the ticket detail page
2. Click the "PDF" or download icon button in the header
3. The PDF will download to your device
The PDF includes:
File Attachments
Download individual files attached to a ticket:
1. Open the ticket detail page
2. Scroll to the Attachments section
3. Click on any file name to download it
Download All Attachments
Some tickets offer a "Download All" option to download all attachments as a single archive.
---
14. Frequently Asked Questions
I can't log in. What should I do?
1. Check your email address — Make sure it matches what you registered with
2. Try Magic Link — Click "Sign in with Magic Link" to avoid password issues
3. Reset your password — Click "Forgot Password?" to get a reset link
4. Account pending? — New accounts require admin approval (usually within 24 hours)
5. Account locked? — After too many failed login attempts, your account is temporarily locked. Wait 15 minutes and try again.
I didn't receive the magic link email.
What does "Pending Signature" mean?
It means a service visit has been completed, but you haven't signed the service document yet. You should have received an email with a signature link, or you can sign through the ticket detail page.
Can I create tickets for other companies?
No. Your portal access is linked to your company's customer account. You can only view and create tickets for your own company.
How do I contact support if the portal isn't working?
Contact your service provider directly using the phone number or email listed in your portal or service documentation.
Can I use the portal on my phone?
Yes! The portal is fully responsive and works on all modern smartphones and tablets. Simply open your phone's web browser and navigate to the portal URL.
---
Previous: Guide 3 — Launch & Verify | Next: Guide 5 — Employee Guide
Corejobtrack Server — Employee Portal Guide
> Audience: Employees (technicians, support staff, team leads) who use the admin portal for day-to-day work — creating tickets, managing tasks, and communicating with customers.
---
Table of Contents
1. Overview
2. Logging In
3. Dashboard
4. Working with Service Tickets
5. Working with Support Tickets
9. Templates
11. Signatures
12. Notifications
13. Search
15. Using the Mobile App (API)
---
1. Overview
As an employee, you use the Admin Portal for daily work. Despite the name "admin," this portal is for all employees — both regular users and administrators. The features available to you depend on your role.
What You Can Do
| Feature | Regular Employee | Admin |
| View assigned tickets & tasks | Yes | Yes |
| Create new tickets & tasks | Yes | Yes |
| Edit tickets & tasks | Yes | Yes |
| View all customers | Yes | Yes |
| Manage customer contacts | Yes | Yes |
| Upload attachments | Yes | Yes |
| Capture signatures | Yes | Yes |
| Generate PDF summaries | Yes | Yes |
| Approve new customer registrations | No | Yes |
| Manage employee accounts | No | Yes |
| Access server administration | No | Yes |
| View server logs | No | Yes |
| Manage trash/deleted items | No | Yes |
Accessing the Portal
Navigate to:
https://tasks.yourcompany.com/portal/admin/login
Or use the unified login at:
https://tasks.yourcompany.com/login
Employee credentials are automatically routed to the admin portal.
---
2. Logging In
Username & Password
1. Enter your username or email address
2. Enter your password
3. Optionally check "Remember me" (stays logged in for 30 days)
4. Click "Sign In"
Magic Link Login
1. Click "Sign in with Magic Link"
2. Enter your email address
3. Check your email and click the link
4. You're logged in — no password needed
!Placeholder: Admin portal login page
First-Time Login
If an admin created your account, use the credentials they provided. You should change your password after first login:
1. Click the Settings icon in the top navigation
2. Go to Password section
3. Enter your current password and set a new one
---
3. Dashboard
The dashboard is your home base. It shows all tickets and tasks at a glance.
!Placeholder: Employee dashboard overview
Navigation Bar
The top navigation includes:
| Tab | What It Shows |
| **Dashboard** | Main view with all tickets and tasks |
| **Notifications** | Your notification center |
| **Customers** | Customer directory |
| **Vendors** | Vendor directory |
| **Contacts** | Customer contact list |
| **Equipment** | Equipment inventory |
| **Templates** | Task and ticket templates |
| **Admin Area** | Server administration (admin role only) |
Dashboard Tables
The dashboard displays three tables:
#### Service Tickets
| Column | Description |
| Ticket # | Auto-generated number (click to open) |
| Title | Brief description |
| Customer | Customer company name |
| Status | Open, In Progress, Completed, Archived |
| Assigned | Who's working on it |
| Created | When it was created |
#### Support Tickets
| Column | Description |
| Ticket # | Auto-generated number (click to open) |
| Title | Brief description |
| Customer | Customer company name |
| Status | Current status |
| Created | When it was created |
#### Tasks
| Column | Description |
| Task # | Auto-generated task code |
| Title | Task description |
| Status | Open, In Progress, Completed |
| Assigned | Who's responsible |
| Due Date | Deadline |
Creating New Items
Click the "New" button to create:
Pending Customer Approvals
If customers have registered and are awaiting approval, you'll see a Pending Approvals section (admin only) with:
---
4. Working with Service Tickets
Service tickets track on-site service visits — equipment repairs, installations, and maintenance.
Creating a Service Ticket
1. Click "New" > "Service Ticket"
2. Fill in the required fields:
3. Optional fields:
4. Click "Create Ticket"
Viewing a Service Ticket
Click any ticket number to open the detail view:
!Placeholder: Service ticket detail view
#### Key Sections
Customer & Contact
Equipment
Time Entries
Track time spent on the service visit:
Services Performed
Free-text field summarizing the work done.
Settings Recorded / Tasks Completed
Checklists of settings that were documented and tasks that were performed.
Attachments
Photos, videos, and documents uploaded during the service visit.
Signature
Customer signature captured after service completion.
Editing a Service Ticket
1. Click the "Edit" button on the ticket detail page
2. Modify any fields
3. Click "Save"
Changes are recorded in the ticket's Change Log (click "View History" to see all changes).
Service Ticket Status Flow
Pending → Open → In Progress → Completed → Archived
↓
Trash
Generating a PDF Summary
Click the PDF icon to download a professional summary including:
---
5. Working with Support Tickets
Support tickets track remote support cases — phone calls, emails, and remote sessions.
Creating a Support Ticket
1. Click "New" > "Support Ticket"
2. Fill in:
3. Optional fields:
4. Click "Create Ticket"
Communication Log
Support tickets include a Communication Log — a conversation thread between your team and the customer.
#### Sending Messages
1. Open the support ticket
2. Scroll to the Communication Log
3. Type your message
4. Click "Send"
#### Features
#### Inline Attachments
Upload files directly into the communication thread:
1. Click the attachment icon next to the message box
2. Select files to upload
3. The attachment appears in the conversation
Support Ticket Status Flow
Pending → Open → In Progress → Completed → Archived
↓
Trash
---
6. Working with Tasks
Tasks are internal work items — things your team needs to do that aren't directly tied to a customer visit.
Creating a Task
#### From a Template
1. Click "New" > "Task from Template"
2. Select a template from the list
3. The task is pre-filled with the template's title, description, steps, and assignments
4. Modify as needed
5. Click "Create"
#### Blank Task
1. Click "New" > "Blank Task"
2. Fill in:
3. Click "Create"
Task Checklist (Steps)
Tasks can have a checklist of steps:
!Placeholder: Task with checklist steps
Each completed step records who completed it and when.
Task Notes
Add notes and comments to tasks:
1. Scroll to the Notes section
2. Type your note
3. Click "Add Note"
Notes are timestamped with the author's name.
Cloning a Task
Click the "Clone" button to create a copy of an existing task with the same details and steps.
---
7. Customer Management
Customer Directory
Navigate to Customers in the top menu to see all customers:
!Placeholder: Customer list view
Customer Detail Page
Each customer page shows:
Adding a New Customer
1. Click "New Customer"
2. Enter company name, address, and contact details
3. Click "Save"
Managing Contacts
Contacts are people at a customer's company. Each customer can have multiple contacts:
#### Contact Actions
For each contact, you can:
---
8. Equipment Tracking
Equipment Directory
Navigate to Equipment in the top menu to see all equipment across all customers.
Equipment on Customer Page
Each customer's equipment is listed on their detail page:
Adding Equipment
1. Go to the customer's detail page
2. Click "Add Equipment"
3. Enter: Make, Model, Serial Number, Count, Notes
4. Click "Save"
Count Tracking
Track equipment counts over time:
1. Open the equipment detail page
2. Click "Add Count"
3. Enter the current count and optional notes
4. The count history shows all changes with who recorded them and when
---
9. Templates
Templates pre-define tasks with steps, assignments, and default values — saving time when creating repetitive tasks.
Viewing Templates
Navigate to Templates to see all available templates.
Creating a Template
1. Click "New Template"
2. Define:
3. Click "Save"
Using a Template
When creating a new task, select "Task from Template" and choose your template. The task is pre-filled with all the template's defaults.
---
10. Attachments & Files
Uploading Files
You can attach files to any ticket or task:
1. Open the ticket or task
2. Find the Attachments section
3. Click "Upload" or drag and drop files
4. Files are uploaded immediately
Supported File Types
| Category | Extensions |
| Documents | PDF, DOC, DOCX, XLS, XLSX, CSV, TXT, RTF, PPT, PPTX |
| Images | PNG, JPG, JPEG, GIF, BMP, TIFF, WebP, SVG, HEIC |
| Video | MP4, MOV, AVI |
| Audio | MP3, WAV |
| Archives | ZIP, RAR, 7Z |
| Apple | Pages, Numbers, Keynote |
File Size Limits
Viewing & Downloading
---
11. Signatures
Capturing On-Site Signatures
When completing a service visit, capture the customer's signature:
1. Open the service ticket
2. Click "Request Signature" or present the signature screen
3. The customer draws their signature on the canvas
4. They enter their printed name
5. Click "Submit"
Requesting a Post-Visit Signature
If the customer wasn't available during the visit:
1. Open the service ticket
2. Click "Request Signature"
3. An email is sent to the customer with a secure link
4. The customer signs online at their convenience
Declining a Signature
If a customer declines to sign:
1. Click "Decline Signature"
2. Select a reason:
3. The ticket records the decline with the reason
Automatic Reminders
Corejobtrack can automatically send reminder emails for unsigned service tickets. These are configured by an administrator.
---
12. Notifications
In-App Notifications
Click the bell icon in the navigation bar to see notifications:
Types of Notifications
| Event | Who Gets Notified |
| Ticket created | Assigned users and teams |
| Ticket status changed | Creator, assigned users |
| Message received | Ticket participants |
| @Mentioned | The mentioned user |
| Ticket assigned to you | You |
| Customer registered | Admins |
| Signature captured | Ticket creator |
Managing Notifications
Push Notifications (Mobile)
If you use the mobile app, register your device for push notifications. You'll receive alerts even when the app isn't open.
---
13. Search
Unified Search
Corejobtrack provides a powerful search across all tickets and tasks:
Search from the API:
GET /search?q=paper+jam&entity_types=service_ticket,support_ticket
Per-Entity Search
Each entity type has its own search endpoint:
Ticket Lookup by Number
Look up any ticket by its auto-generated number:
GET /ticket-number/456789
---
14. Your Profile & Settings
Accessing Settings
Click the gear icon or Settings in the navigation.
Profile Information
Update your personal details:
Profile Picture
Change Password
1. Enter your current password
2. Enter and confirm your new password
3. Click "Change Password"
Chat Color
Set a custom color for your name in communication logs. This helps identify messages at a glance.
---
15. Using the Mobile App (API)
The Corejobtrack mobile app connects via the same REST API as the portal. Key operations:
Authentication
POST /token
Returns an access token for subsequent requests.
Viewing Your Assignments
GET /tasks/ — Your visible tasks
GET /service-tickets/ — Service tickets
GET /support-tickets/ — Support tickets
Managing Notifications
GET /notifications — Your notifications
POST /notifications/{id}/read — Mark as read
Device Registration (Push Notifications)
POST /devices/register
Register your device token to receive push notifications.
For complete API documentation, see the API Endpoints Reference.
---
Previous: Guide 4 — Customer Portal | Next: Guide 6 — Admin Guide
Corejobtrack Server — Admin Guide
> Audience: Administrators who manage the Corejobtrack system — user accounts, server configuration, security, and system maintenance.
---
Table of Contents
4. Customer Registration Approvals
8. Push Notification (APNs) Management
10. Embedding / AI Search Management
11. SMB Hot Folder Configuration
12. Trash & Recovery
13. Log Management
14. System Settings
---
1. Admin Role Overview
Admin users have full access to all Corejobtrack features plus system administration capabilities. Key admin-only functions:
| Function | Description |
| User Management | Create, edit, delete employee accounts |
| Registration Approvals | Approve/deny customer registrations |
| Server Administration | Restart, deploy, view logs |
| SSL Management | View/renew SSL certificates |
| Email Configuration | Monitor email service status |
| APNs Configuration | Configure push notifications |
| Database Monitoring | View schema status, pool stats |
| AI/Search Configuration | Manage embedding service |
| Trash Management | Restore/permanently delete items |
| System Settings | Configure defaults, dropdown choices |
Becoming an Admin
Admins are assigned the admin role in one of two ways:
1. Database update (first admin):
UPDATE users SET role = 'admin', is_admin = TRUE WHERE username = 'your_username';
2. Admin promotes another user (subsequent admins):
curl -X PUT https://server:8443/users/USER_ID \
-H "Authorization: Bearer ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"role": "admin"}'
---
2. Accessing the Admin Area
Via Portal
1. Log into the admin portal
2. Click "Admin Area" in the navigation bar (visible only to admins)
!Placeholder: Admin Area dashboard
The Admin Area provides a visual interface for:
Via API
All admin endpoints are prefixed with /admin/ and require the require_admin dependency.
---
3. User Management
Viewing All Users
Portal: Navigate to Admin Area > Users
API:
curl https://server:8443/users/ \
-H "Authorization: Bearer ADMIN_TOKEN"
Returns all users with full details: username, role, email, team, status.
Creating a New Employee
Portal: Click "New User" in the Admin Area > Users section
API:
curl -X POST https://server:8443/users/ \
-H "Authorization: Bearer ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"username": "jdoe",
"password": "SecurePass123!",
"full_name": "John Doe",
"first_name": "John",
"last_name": "Doe",
"email": "[email protected]",
"phone": "(555) 234-5678",
"team": "Service"
}'
Editing a User
API:
curl -X PUT https://server:8443/users/USER_ID \
-H "Authorization: Bearer ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"role": "admin",
"team": "Management",
"new_password": "NewSecurePass!"
}'
Available fields to update:
Approving Pending Users
When employees self-register via POST /register, their accounts start with role: "pending" and cannot access anything until approved:
curl -X PUT https://server:8443/users/USER_ID \
-H "Authorization: Bearer ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"role": "user"}'
Deleting a User
curl -X DELETE https://server:8443/users/USER_ID \
-H "Authorization: Bearer ADMIN_TOKEN"
> Note: Deleting a user reassigns all items created by that user to the admin who performed the deletion.
User Roles Summary
| Role | Can Login | API Access | Portal Access | Admin Area |
| `pending` | No | No | No | No |
| `user` | Yes | Yes | Yes | No |
| `admin` | Yes | Yes | Yes | Yes |
---
4. Customer Registration Approvals
When customers register through the portal, they appear in the Pending Approvals section on the admin dashboard.
From the Portal
1. Open the admin dashboard
2. Scroll to Pending Customer Approvals
3. Review the customer's name, email, and company
4. Click "Approve" to grant access, or "Deny" to reject
!Placeholder: Pending approvals section
What Happens on Approval
What Happens on Denial
Managing Contacts
From the Admin Portal, you can also:
Navigate to Contacts or the customer detail page to access these actions.
---
5. Server Administration
Server Restart / Deploy
The admin portal and API provide remote deployment capabilities.
#### Via Portal
1. Navigate to Admin Area
2. Click "Restart Server"
3. Confirm in the dialog
4. Wait for the server to restart (you'll briefly lose connection)
#### Via API
curl -X POST https://server:8443/admin/restart \
-H "Authorization: Bearer ADMIN_TOKEN"
What Happens:
1. The restart_services.sh script is triggered
2. Latest code is pulled from git
3. Dependencies are updated
4. Systemd services are restarted
5. Deployment report is emailed to ADMIN_EMAIL
Checking Deploy Status
After a restart, check the deployment result:
curl https://server:8443/admin/deploy-status \
-H "Authorization: Bearer ADMIN_TOKEN"
Response:
{
"last_deploy": {
"status": "success",
"version_before": "0.980",
"version_after": "0.981",
"branch": "main",
"commit_count": 3,
"commits": [
{"hash": "abc1234", "message": "Fix login bug", "author": "dev"}
],
"files_changed": "5 files changed",
"initiated_at": "2026-03-01T10:00:00Z",
"completed_at": "2026-03-01T10:01:30Z",
"admin_username": "admin"
}
}
---
6. SSL Certificate Management
View Certificate Info
curl https://server:8443/admin/ssl/cert-info \
-H "Authorization: Bearer ADMIN_TOKEN"
Response:
{
"exists": true,
"valid_from": "2026-01-01T00:00:00+00:00",
"valid_until": "2027-01-01T00:00:00+00:00",
"days_remaining": 305,
"expires_soon": false,
"expired": false,
"path": "/opt/taskapp/app/server_cert.pem"
}
Key fields:
Renew Certificate
curl -X POST https://server:8443/admin/ssl/renew-cert \
-H "Authorization: Bearer ADMIN_TOKEN"
This runs the generate_cert.py script and restarts the SSL service.
> Important: Set up monitoring for expires_soon to avoid service interruptions.
---
7. Email Service Management
Check Email Status
curl https://server:8443/email/status \
-H "Authorization: Bearer ADMIN_TOKEN"
View Email Log
Portal: Admin Area > Email Log
API:
# Paginated email log
curl "https://server:8443/email/log?limit=50&offset=0" \
-H "Authorization: Bearer ADMIN_TOKEN"
# Filter by status
curl "https://server:8443/email/log?status=failed" \
-H "Authorization: Bearer ADMIN_TOKEN"
# Get stats
curl https://server:8443/email/log/stats \
-H "Authorization: Bearer ADMIN_TOKEN"
Email Dev Mode
When SMTP_DEV_MODE=true:
Send a Test Email
curl -X POST https://server:8443/email/send \
-H "Authorization: Bearer ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"to": "[email protected]",
"subject": "Corejobtrack Email Test",
"body_text": "Test email from Corejobtrack",
"body_html": "<h1>Test</h1><p>Email is working.</p>"
}'
---
8. Push Notification (APNs) Management
Check APNs Status
curl https://server:8443/admin/apns/status \
-H "Authorization: Bearer ADMIN_TOKEN"
Response:
{
"configured": true,
"sandbox_mode": false,
"bundle_id": "com.company.corejobtrack",
"key_id_set": true,
"team_id_set": true,
"key_path_set": true,
"httpx_available": true,
"active_tokens": 15,
"users_with_tokens": 8,
"status": {
"configured": true,
"last_push_at": "2026-03-01T10:00:00Z",
"pushes_sent": 150,
"pushes_failed": 2
}
}
Configure APNs
curl -X POST https://server:8443/admin/apns/config \
-H "Authorization: Bearer ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"key_id": "ABC1234567",
"team_id": "DEF1234567",
"bundle_id": "com.company.corejobtrack",
"use_sandbox": false,
"key_content": "BASE64_ENCODED_P8_KEY"
}'
The .p8 key file can be uploaded as base64-encoded content. The server saves it to disk.
Send a Test Push
curl -X POST https://server:8443/admin/apns/test \
-H "Authorization: Bearer ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"user_id": 1,
"title": "Test Notification",
"body": "This is a test push notification"
}'
If user_id is omitted, the notification is sent to the admin's own devices.
---
9. Database Monitoring
Health Status
# Basic health
curl https://server:8443/dbhealth
# Detailed status with recovery tracking
curl https://server:8443/db-status
# Connection pool statistics (admin only)
curl https://server:8443/db-pool-stats \
-H "Authorization: Bearer ADMIN_TOKEN"
Schema Status
Check which optional columns exist and get SQL to fix missing ones:
curl https://server:8443/admin/db/schema-status \
-H "Authorization: Bearer ADMIN_TOKEN"
If missing_sql contains entries, you can either:
Pool Statistics
{
"available": 5, // Idle connections waiting
"in_use": 2, // Connections currently serving requests
"max": 20, // Maximum pool size
"min": 2 // Minimum connections to maintain
}
Warning signs:
---
10. Embedding / AI Search Management
Corejobtrack supports AI-powered natural language search using OpenAI embeddings.
Check Status
curl https://server:8443/admin/embedding/status \
-H "Authorization: Bearer ADMIN_TOKEN"
Response:
{
"enabled": true,
"key_configured": true,
"key_hint": "...a1b2",
"model": "text-embedding-3-small",
"dimensions": 1536,
"pgvector_available": true,
"usage": {
"total_tokens": 50000,
"total_cost": 0.25,
"total_embeddings": 500,
"budget_cap": 5.0
}
}
Enable/Disable
curl -X POST https://server:8443/admin/embedding/toggle \
-H "Authorization: Bearer ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"enabled": true}'
Set API Key
curl -X POST https://server:8443/admin/embedding/api-key \
-H "Authorization: Bearer ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"api_key": "sk-your-openai-key"}'
Test API Key
curl -X POST https://server:8443/admin/embedding/test-key \
-H "Authorization: Bearer ADMIN_TOKEN"
Manage Budget
# Set budget cap
curl -X PUT https://server:8443/admin/embedding/budget \
-H "Authorization: Bearer ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"budget_cap": 10.0}'
# Reset usage counters
curl -X POST https://server:8443/admin/embedding/reset-usage \
-H "Authorization: Bearer ADMIN_TOKEN"
Backfill Existing Data
When enabling embeddings for the first time, run a backfill to generate embeddings for existing data:
# Check backfill status
curl https://server:8443/admin/embedding/backfill-status \
-H "Authorization: Bearer ADMIN_TOKEN"
# Start backfill manually
curl -X POST https://server:8443/admin/embedding/backfill-start \
-H "Authorization: Bearer ADMIN_TOKEN"
---
11. SMB Hot Folder Configuration
SMB Watch monitors a shared folder (SMB network share or local directory) for JSON files and automatically creates tasks or tickets.
Check Status
curl https://server:8443/admin/smb-watch/status \
-H "Authorization: Bearer ADMIN_TOKEN"
Configure
curl -X POST https://server:8443/admin/smb-watch/config \
-H "Authorization: Bearer ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"enabled": true,
"path": "//server/share/hotfolder",
"username": "smb_user",
"password": "smb_password",
"file_type": "json",
"interval_seconds": 30
}'
| Field | Description |
| `enabled` | Toggle monitoring on/off |
| `path` | UNC path (`//server/share/`) or local path (`/path/to/folder`) |
| `username` | SMB authentication username |
| `password` | SMB authentication password |
| `file_type` | File format to process (currently only `json`) |
| `interval_seconds` | How often to check for new files (minimum: 5) |
How It Works
1. JSON files placed in the hot folder are detected
2. The file content is parsed and validated
3. A task, service ticket, or support ticket is created automatically
4. Processed files are moved to a processed/ subdirectory
5. Failed files are moved to a failed/ subdirectory
> Requirement: The smbprotocol / smbclient Python package must be installed for SMB (network) paths. Local paths work without it.
---
12. Trash & Recovery
Deleted items are soft-deleted (moved to "trash" status) and can be recovered by admins.
View Trash
Portal: Admin Area > Trash
API:
curl https://server:8443/admin/trash \
-H "Authorization: Bearer ADMIN_TOKEN"
# Filter by type
curl "https://server:8443/admin/trash?item_type=service_ticket" \
-H "Authorization: Bearer ADMIN_TOKEN"
Restore an Item
curl -X POST https://server:8443/admin/trash/ITEM_ID/restore \
-H "Authorization: Bearer ADMIN_TOKEN"
The item is restored to its previous status (typically "open").
Permanently Delete
curl -X DELETE https://server:8443/admin/trash/ITEM_ID \
-H "Authorization: Bearer ADMIN_TOKEN"
> Warning: Permanent deletion cannot be undone.
Empty All Trash
curl -X POST https://server:8443/admin/trash/empty \
-H "Authorization: Bearer ADMIN_TOKEN"
> Warning: This permanently deletes ALL items in trash.
---
13. Log Management
View Server Logs
Portal: Admin Area > Server Log
API:
# Last 200 lines
curl "https://server:8443/logs?lines=200" \
-H "Authorization: Bearer ADMIN_TOKEN"
Export Logs
curl "https://server:8443/logs/export?log_type=server&date_from=2026-02-01&date_to=2026-03-01" \
-H "Authorization: Bearer ADMIN_TOKEN"
Parameters:
Email Log
View all sent emails and their delivery status:
Portal: Admin Area > Email Log
API:
curl "https://server:8443/email/log?limit=50&status=failed" \
-H "Authorization: Bearer ADMIN_TOKEN"
Client Logs
Client applications submit logs to the server for centralized tracking:
# View submitted client logs in the export
curl "https://server:8443/logs/export?log_type=client&date_from=2026-02-28" \
-H "Authorization: Bearer ADMIN_TOKEN"
---
14. System Settings
Ticket Color Defaults
Set the default colors for new tickets:
# View defaults
curl https://server:8443/settings/ticket-defaults \
-H "Authorization: Bearer ADMIN_TOKEN"
# Update defaults
curl -X PUT https://server:8443/settings/ticket-defaults \
-H "Authorization: Bearer ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"service_ticket": "#4A90D9",
"support_ticket": "#E67E22"
}'
Dropdown Choices
Configure the dropdown options for various fields:
# Get choices for a field
curl https://server:8443/settings/dropdown-choices/dispatch_type \
-H "Authorization: Bearer ADMIN_TOKEN"
# Set choices
curl -X PUT https://server:8443/settings/dropdown-choices/dispatch_type \
-H "Authorization: Bearer ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '[
{"value": "Emergency", "label": "Emergency"},
{"value": "Scheduled", "label": "Scheduled"},
{"value": "PM", "label": "Preventive Maintenance"}
]'
Task Types
Manage the available task type categories:
# List task types
curl https://server:8443/task_types/ \
-H "Authorization: Bearer ADMIN_TOKEN"
# Add a task type
curl -X POST https://server:8443/task_types/ \
-H "Authorization: Bearer ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Installation"}'
# Delete a task type
curl -X DELETE https://server:8443/task_types/Installation \
-H "Authorization: Bearer ADMIN_TOKEN"
Signature Settings
Configure post-visit signature reminders in taskapp.env:
ENABLE_SIGNATURE_REMINDERS=true
SIGNATURE_TOKEN_EXPIRY_DAYS=7
Trigger reminders manually:
curl -X POST https://server:8443/admin/send-signature-reminders \
-H "Authorization: Bearer ADMIN_TOKEN"
---
15. Deployment & Updates
Automated Deployment
Corejobtrack supports one-click deployment from the admin portal or API:
curl -X POST https://server:8443/admin/restart \
-H "Authorization: Bearer ADMIN_TOKEN"
Deployment Flow
1. Admin triggers restart
2. Metadata saved (who, when, current version)
3. restart_services.sh executed:
a. git pull (fetch latest code)
b. pip install (update dependencies)
c. systemctl restart (restart services)
4. Server starts with new code
5. ensure_schema() runs (auto-migration)
6. Deployment report emailed to ADMIN_EMAIL
7. Client can query /admin/deploy-status
Version Tracking
# Current version
curl https://server:8443/version
# Last deployment details
curl https://server:8443/admin/deploy-status \
-H "Authorization: Bearer ADMIN_TOKEN"
---
16. Security Configuration
Rate Limiting
Configured via environment variables:
| Variable | Default | Description |
| `RATE_LIMIT_ENABLED` | `true` | Enable/disable rate limiting |
| `RATE_LIMIT_LOGIN_MAX` | `5` | Max login attempts per window |
| `RATE_LIMIT_LOGIN_WINDOW` | `300` | Window in seconds (5 min) |
| `RATE_LIMIT_REGISTER_MAX` | `3` | Max registrations per window |
| `RATE_LIMIT_REGISTER_WINDOW` | `3600` | Window in seconds (1 hour) |
| `RATE_LIMIT_GENERAL_MAX` | `100` | Max general requests per window |
| `RATE_LIMIT_GENERAL_WINDOW` | `60` | Window in seconds (1 min) |
Account Lockout
| Variable | Default | Description |
| `ACCOUNT_LOCKOUT_ENABLED` | `true` | Enable/disable lockout |
| `ACCOUNT_LOCKOUT_THRESHOLD` | `10` | Failures before lockout |
| `ACCOUNT_LOCKOUT_DURATION` | `900` | Lockout duration (15 min) |
CORS Policy
| Variable | Default | Description |
| `CORS_ORIGINS` | `*` | Allowed origins (comma-separated) |
| `CORS_ALLOW_CREDENTIALS` | `true` | Allow cookies/auth headers |
> Warning: Using CORS_ORIGINS=* with CORS_ALLOW_CREDENTIALS=true is insecure. Set specific origins in production.
Security Status
View all security settings at a glance:
curl https://server:8443/security-status \
-H "Authorization: Bearer ADMIN_TOKEN"
Password Policy
| Variable | Default | Description |
| `MIN_PASSWORD_LENGTH` | `8` | Minimum password length |
| `REQUIRE_PASSWORD_COMPLEXITY` | `true` | Require uppercase, lowercase, numbers, symbols |
---
Previous: Guide 5 — Employee Guide | Next: Guide 7 — API Endpoints
Corejobtrack Server — API Endpoints Reference
> Audience: Developers building client applications, integrations, or automations against the Corejobtrack REST API.
---
Table of Contents
1. API Overview
3. Public Endpoints (No Auth Required)
4. Employee Endpoints (JWT Required)
5. Admin Endpoints (Admin Role Required)
6. Customer Endpoints (Customer JWT Required)
10. Pagination
---
1. API Overview
Base URL
https://your-server:8443
Content Type
All request and response bodies use JSON:
Content-Type: application/json
Exceptions:
Authentication Header
Authorization: Bearer YOUR_ACCESS_TOKEN
Response Format
Success:
{"ok": true, "id": 123, "status": "open"}
Error:
{"detail": "Error message description"}
Server Error (500):
{"detail": "Internal server error", "error_id": "a1b2c3d4"}
The error_id is a hex string that can be used to correlate with server logs.
Interactive API Docs
FastAPI provides automatic interactive documentation:
---
2. Authentication
Employee Login
POST /token
Content-Type: application/x-www-form-urlencoded
username=admin&password=SecurePass123
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "bearer"
}
Token Refresh
POST /token/refresh
Authorization: Bearer YOUR_TOKEN
Returns a new access token with refreshed expiration.
Token Expiration
Employee Registration
POST /register
Content-Type: application/json
{
"username": "jdoe",
"password": "SecurePass123!",
"full_name": "John Doe",
"first_name": "John",
"last_name": "Doe",
"email": "[email protected]",
"phone": "(555) 234-5678",
"team": "Service"
}
New accounts start with role: "pending" — an admin must approve.
---
3. Public Endpoints (No Auth Required)
These endpoints are accessible without any authentication.
Health & Status
| Method | Endpoint | Description |
| `GET` | `/health` | Basic health check — returns `{"ok": true}` |
| `GET` | `/version` | Server version — returns `{"version": "0.981"}` |
| `GET` | `/dbhealth` | Database connectivity check |
| `GET` | `/db-status` | Detailed DB status with recovery tracking |
Authentication
| Method | Endpoint | Description |
| `POST` | `/register` | Employee self-registration |
| `POST` | `/token` | Employee login (returns JWT) |
Customer Authentication
| Method | Endpoint | Description |
| `POST` | `/customer/api/register` | Customer registration (join existing company) |
| `POST` | `/customer/api/login` | Customer login |
| `POST` | `/customer/api/magic-link` | Request magic link email |
| `POST` | `/customer/api/verify-magic-link` | Verify magic link token |
| `POST` | `/customer/api/password-reset` | Request password reset |
Feedback
| Method | Endpoint | Description |
| `POST` | `/feedback/` | Submit feedback or bug report (auth optional) |
---
4. Employee Endpoints (JWT Required)
All endpoints in this section require Authorization: Bearer TOKEN with a valid employee JWT.
Current User Profile
| Method | Endpoint | Description |
| `GET` | `/me` | Get current user profile with teams |
| `PUT` | `/me` | Update profile (name, email, phone, chat_color) |
| `POST` | `/me/delete-account` | Request account deactivation |
| `POST` | `/me/profile-picture` | Upload profile picture |
| `DELETE` | `/me/profile-picture` | Delete profile picture |
| `GET` | `/users/{user_id}/profile-picture` | Get any user's profile picture |
| `POST` | `/token/refresh` | Refresh access token |
PUT /me Request Body:
{
"full_name": "John Doe",
"first_name": "John",
"last_name": "Doe",
"nick_name": "JD",
"email": "[email protected]",
"phone": "(555) 234-5678",
"team": "Service",
"chat_color": "#FF5733",
"current_password": "old_pass",
"new_password": "new_pass"
}
User List
| Method | Endpoint | Description |
| `GET` | `/users/list` | Public user list (for assignment dropdowns) |
---
Tasks
| Method | Endpoint | Description |
| `GET` | `/tasks/` | List visible tasks (paginated) |
| `GET` | `/tasks/{task_id}` | Get single task with assignments |
| `POST` | `/tasks/` | Create new task |
| `PATCH` | `/tasks/{task_id}` | Update task fields |
| `PUT` | `/tasks/{task_id}` | Update task (PATCH alias) |
| `DELETE` | `/tasks/{task_id}` | Soft-delete task (→ trash) |
| `POST` | `/tasks/{task_id}/pause` | Pause task |
| `POST` | `/tasks/{task_id}/resume` | Resume task |
| `POST` | `/tasks/{task_id}/archive` | Archive task |
| `POST` | `/tasks/{task_id}/complete` | Complete task |
| `GET` | `/tasks/{task_id}/changelog` | Get change history |
| `GET` | `/tasks/search/{query}` | Full-text search tasks |
POST /tasks/ Request Body:
{
"title": "Replace drum unit",
"description": "Customer reports streaking on prints",
"status": "open",
"urgency": "Medium",
"visibility": "private",
"task_type": "Repair",
"color": "#4A90D9",
"due_date": "2026-03-15",
"assigned_to": 5,
"assigned_user_ids": [5, 8],
"assigned_teams": ["Service"],
"customer_name": "Acme Corp",
"customer_number": "CUST001",
"customer_address_1": "123 Main St",
"city": "Anytown",
"state": "ST",
"zip": "12345",
"customer_contact_first_name": "Jane",
"customer_contact_last_name": "Doe",
"customer_phone": "(555) 123-4567",
"customer_email": "[email protected]",
"equipment": [
{
"make": "HP",
"model": "LaserJet Pro",
"serial": "ABC123",
"count": 1,
"problem": "Streaking"
}
],
"template_id": 3
}
Task Steps (Checklists)
| Method | Endpoint | Description |
| `GET` | `/tasks/{task_id}/steps/` | List steps |
| `POST` | `/tasks/{task_id}/steps/` | Add step |
| `PATCH` | `/tasks/{task_id}/steps/{step_id}` | Update step |
POST Step:
{
"step_description": "Remove old drum unit",
"is_completed": false,
"order_num": 1
}
Task Notes
| Method | Endpoint | Description |
| `GET` | `/tasks/{task_id}/notes/` | List notes |
| `POST` | `/tasks/{task_id}/notes/` | Add note |
| `DELETE` | `/tasks/{task_id}/notes/{note_id}` | Delete own note |
POST Note:
{
"note": "Called customer to schedule — available Thursday"
}
---
Service Tickets
| Method | Endpoint | Description |
| `GET` | `/service-tickets/` | List visible service tickets |
| `GET` | `/service-tickets/{ticket_id}` | Get single ticket |
| `POST` | `/service-tickets/` | Create service ticket |
| `PATCH` | `/service-tickets/{ticket_id}` | Update ticket |
| `DELETE` | `/service-tickets/{ticket_id}` | Soft-delete (→ trash) |
| `POST` | `/service-tickets/{ticket_id}/archive` | Archive ticket |
| `POST` | `/service-tickets/{ticket_id}/complete` | Complete ticket |
| `POST` | `/service-tickets/{ticket_id}/restore` | Restore from archive |
| `GET` | `/service-tickets/{ticket_id}/summary.pdf` | Download PDF summary |
| `GET` | `/service-tickets/{ticket_id}/changelog` | Get change history |
POST /service-tickets/ Request Body:
{
"customer_name": "Acme Corp",
"title": "PM Visit - Q1 2026",
"description": "Quarterly preventive maintenance",
"status": "Pending",
"urgency": "Medium",
"visibility": "private",
"task_type": "Maintenance",
"due_date": "2026-03-15",
"assigned_user_ids": [5],
"assigned_teams": ["Service"],
"dispatch_type": "Scheduled",
"dispatch_number": "D-2026-001",
"customer_po": "PO-12345",
"equipment": [
{
"make": "Konica Minolta",
"model": "bizhub C300i",
"serial": "A9K123456",
"count": 1,
"problem": "PM Due",
"problem_description": "Quarterly maintenance scheduled"
}
],
"session_timestamps": [
{
"start_time": "2026-03-01T09:00:00",
"stop_time": "2026-03-01T12:30:00",
"time_amount": 3.5,
"time_type": "regular",
"num_techs": 1,
"lunch": 0
}
],
"services_performed": "Cleaned components, replaced rollers, calibrated",
"liftgate": false,
"sprinter": true,
"remote": false,
"phone_call": false
}
Signatures
| Method | Endpoint | Description |
| `POST` | `/service-tickets/{ticket_id}/sign` | Submit signature |
| `POST` | `/service-tickets/{ticket_id}/decline-signature` | Decline signature |
| `POST` | `/service-tickets/{ticket_id}/request-signature` | Send signature email |
POST /sign Request Body:
{
"signature_data": "data:image/png;base64,iVBOR...",
"signature_name": "Jane Doe",
"send_summary_email": true,
"safety_covers_ok": true,
"safety_covers_notes": null
}
POST /decline-signature Request Body:
{
"decline_reason": "Contact not around",
"send_summary_email": false,
"send_signature_request": true
}
Valid decline reasons: "Contact not around", "Contact refused to sign", "Contact prefers to sign online", "Other"
---
Support Tickets
| Method | Endpoint | Description |
| `GET` | `/support-tickets/` | List support tickets |
| `GET` | `/support-tickets/{ticket_id}` | Get single ticket |
| `POST` | `/support-tickets/` | Create support ticket |
| `PATCH` | `/support-tickets/{ticket_id}` | Update ticket |
| `DELETE` | `/support-tickets/{ticket_id}` | Soft-delete (→ trash) |
| `POST` | `/support-tickets/{ticket_id}/archive` | Archive |
| `POST` | `/support-tickets/{ticket_id}/complete` | Complete |
| `POST` | `/support-tickets/{ticket_id}/restore` | Restore from archive |
| `POST` | `/support-tickets/{ticket_id}/communication` | Add communication message |
| `GET` | `/support-tickets/{ticket_id}/comm-readers` | Get read receipts |
| `GET` | `/support-tickets/{ticket_id}/summary.pdf` | Download PDF |
| `GET` | `/support-tickets/{ticket_id}/changelog` | Get change history |
POST /communication Request Body:
{
"message": "I've escalated this to the vendor. Will follow up tomorrow.",
"time_spent": 0.5,
"is_customer": false,
"author_user_id": null
}
---
Customers
| Method | Endpoint | Description |
| `GET` | `/customers/list` | List customers (paginated) |
| `GET` | `/customers/search` | Search by name/contact_id/address |
| `GET` | `/customers/{customer_id}` | Get customer with contacts |
| `PATCH` | `/customers/{customer_id}` | Update customer |
| `DELETE` | `/customers/{customer_id}` | Delete customer (cascades) |
| `POST` | `/customers/{customer_id}/contacts` | Create contact |
| `PATCH` | `/customer-contacts/{contact_id}` | Update contact |
| `DELETE` | `/customer-contacts/{contact_id}` | Delete contact |
| `POST` | `/customers/{customer_id}/contacts/reorder` | Reorder contacts |
GET /customers/search Query Parameters:
---
Equipment
| Method | Endpoint | Description |
| `GET` | `/customers/{customer_id}/equipment` | List equipment |
| `POST` | `/customers/{customer_id}/equipment` | Add equipment |
| `PATCH` | `/customer-equipment/{equipment_id}` | Update equipment |
| `DELETE` | `/customer-equipment/{equipment_id}` | Delete equipment |
| `POST` | `/customer-equipment/{equipment_id}/counts` | Log count |
| `GET` | `/customer-equipment/{equipment_id}/counts` | Get count history |
POST Equipment:
{
"make": "HP",
"model": "LaserJet Pro M428",
"serial": "CNBJ1234",
"count": 1,
"notes": "Located in 2nd floor copy room"
}
---
Vendors
| Method | Endpoint | Description |
| `GET` | `/vendors/list` | List vendors (paginated) |
| `GET` | `/vendors/search` | Search vendors |
---
Attachments
| Method | Endpoint | Description |
| `POST` | `/attachments/{entity_type}/{entity_id}/upload` | Upload file(s) |
| `GET` | `/attachments/download/{attachment_id}` | Download file |
| `GET` | `/attachments/{entity_type}/{entity_id}` | List attachments |
| `DELETE` | `/attachments/{attachment_id}` | Delete attachment |
Upload uses multipart/form-data:
curl -X POST https://server:8443/attachments/service_ticket/123/upload \
-H "Authorization: Bearer TOKEN" \
-F "[email protected]"
Entity types: task, service_ticket, support_ticket
---
Notifications
| Method | Endpoint | Description |
| `GET` | `/notifications` | Get notifications |
| `POST` | `/notifications/{id}/read` | Mark as read |
| `POST` | `/notifications/read-all` | Mark all as read |
| `DELETE` | `/notifications/{id}` | Delete notification |
GET /notifications Query Parameters:
---
Ticket Read Status
| Method | Endpoint | Description |
| `GET` | `/tickets/{type}/{id}/read-status` | Get section read status |
| `POST` | `/tickets/{type}/{id}/mark-read` | Mark sections as read |
POST /mark-read:
{
"sections": ["details", "notes", "messages", "attachments"]
}
Ticket types: task, service_ticket, support_ticket
---
Typing Indicators
| Method | Endpoint | Description |
| `POST` | `/tickets/{type}/{id}/typing` | Record user typing |
| `GET` | `/tickets/{type}/{id}/typing` | Get who's typing |
---
Archived Items
| Method | Endpoint | Description |
| `GET` | `/archived/` | List archived items |
| `GET` | `/archived/{id}/` | Get archived item |
| `PATCH` | `/archived/{id}/` | Update archived item |
| `POST` | `/archived/{id}/restore` | Restore to open |
| `DELETE` | `/archived/{id}/` | Move to trash |
---
Related Items
| Method | Endpoint | Description |
| `GET` | `/customers/{customer_id}/related-items` | Tickets for customer |
| `GET` | `/equipment/{equipment_id}/related-items` | Tickets for equipment |
---
Templates
| Method | Endpoint | Description |
| `GET` | `/templates/` | List templates |
| `POST` | `/templates/` | Create template |
| `PUT` | `/templates/{template_id}` | Update template |
| `DELETE` | `/templates/{template_id}` | Delete template |
POST Template:
{
"name": "Quarterly PM",
"title": "PM Visit - {{customer}}",
"description": "Quarterly preventive maintenance visit",
"steps": [
{"step_description": "Clean all components", "is_completed": false, "order_num": 1},
{"step_description": "Replace rollers", "is_completed": false, "order_num": 2},
{"step_description": "Run calibration", "is_completed": false, "order_num": 3}
],
"color": "#27AE60",
"task_type": "Maintenance",
"due_date_offset": 7,
"assigned_user_ids": [5],
"assigned_teams": ["Service"]
}
---
Ticket Lookup
| Method | Endpoint | Description |
| `GET` | `/ticket-number/{number}` | Find ticket by number |
---
Search
| Method | Endpoint | Description |
| `GET` | `/search` | Natural language / keyword search |
Query Parameters:
---
Leads
| Method | Endpoint | Description |
| `GET` | `/leads` | List leads (paginated) |
| `GET` | `/leads/{lead_id}` | Get single lead |
| `POST` | `/leads` | Create lead |
| `PATCH` | `/leads/{lead_id}` | Update lead |
| `DELETE` | `/leads/{lead_id}` | Delete lead |
---
Quotes
| Method | Endpoint | Description |
| `GET` | `/quotes` | List quotes |
| `GET` | `/quotes/{quote_id}` | Get single quote |
| `POST` | `/quotes` | Create quote |
| `PATCH` | `/quotes/{quote_id}` | Update quote |
| `DELETE` | `/quotes/{quote_id}` | Delete quote |
---
Lease Rates
| Method | Endpoint | Description |
| `GET` | `/lease-rates` | List lease rates |
| `POST` | `/lease-rates` | Create lease rate |
| `PATCH` | `/lease-rates/{id}` | Update lease rate |
| `DELETE` | `/lease-rates/{id}` | Delete lease rate |
---
Device Tokens (Push Notifications)
| Method | Endpoint | Description |
| `POST` | `/devices/register` | Register device for push |
| `DELETE` | `/devices/{token}` | Unregister device |
| `GET` | `/devices` | List registered devices |
POST /devices/register:
{
"token": "device_token_from_apns",
"platform": "ios",
"device_name": "iPhone 15 Pro",
"app_version": "2.1.0"
}
---
Settings
| Method | Endpoint | Description |
| `GET` | `/settings/ticket-defaults` | Get ticket color defaults |
| `GET` | `/settings/dropdown-choices/{field}` | Get dropdown options |
| `PUT` | `/settings/dropdown-choices/{field}` | Update dropdown options |
| `GET` | `/task_types/` | List task types |
| `POST` | `/task_types/` | Add task type |
| `PUT` | `/task_types/{old_name}` | Rename task type |
| `DELETE` | `/task_types/{name}` | Delete task type |
---
| Method | Endpoint | Description |
| `POST` | `/email/send` | Queue email for sending |
---
Logs (Client Submission)
| Method | Endpoint | Description |
| `POST` | `/logs` | Submit single client log |
| `POST` | `/log` | Submit single client log (alias) |
| `POST` | `/logs/batch` | Submit batch of client logs |
---
5. Admin Endpoints (Admin Role Required)
All endpoints require both a valid JWT and is_admin: true.
User Management
| Method | Endpoint | Description |
| `GET` | `/users/` | List all users with full details |
| `POST` | `/users/` | Create new user |
| `PUT` | `/users/{user_id}` | Update user |
| `DELETE` | `/users/{user_id}` | Delete user |
| `DELETE` | `/users/{user_id}/profile-picture` | Delete user's picture |
Trash Management
| Method | Endpoint | Description |
| `GET` | `/admin/trash` | List trashed items |
| `POST` | `/admin/trash/{id}/restore` | Restore item |
| `DELETE` | `/admin/trash/{id}` | Permanently delete |
| `POST` | `/admin/trash/empty` | Empty all trash |
Server Administration
| Method | Endpoint | Description |
| `POST` | `/admin/restart` | Deploy & restart server |
| `GET` | `/admin/deploy-status` | Last deployment details |
SSL Certificate
| Method | Endpoint | Description |
| `GET` | `/admin/ssl/cert-info` | Certificate details |
| `POST` | `/admin/ssl/renew-cert` | Regenerate certificate |
APNs Management
| Method | Endpoint | Description |
| `GET` | `/admin/apns/status` | APNs status & stats |
| `GET` | `/admin/apns/config` | Current APNs config |
| `POST` | `/admin/apns/config` | Save APNs config |
| `POST` | `/admin/apns/test` | Send test push |
Database
| Method | Endpoint | Description |
| `GET` | `/admin/db/schema-status` | Schema diagnostics |
| `GET` | `/db-pool-stats` | Connection pool stats |
| `GET` | `/security-status` | Security config status |
Embedding / AI Search
| Method | Endpoint | Description |
| `GET` | `/admin/embedding/status` | Embedding service status |
| `POST` | `/admin/embedding/toggle` | Enable/disable embeddings |
| `POST` | `/admin/embedding/api-key` | Save OpenAI API key |
| `DELETE` | `/admin/embedding/api-key` | Remove API key |
| `POST` | `/admin/embedding/test-key` | Validate API key |
| `PUT` | `/admin/embedding/budget` | Set budget cap |
| `POST` | `/admin/embedding/reset-usage` | Reset usage counters |
| `GET` | `/admin/embedding/backfill-status` | Backfill progress |
| `POST` | `/admin/embedding/backfill-start` | Start backfill |
SMB Watch
| Method | Endpoint | Description |
| `GET` | `/admin/smb-watch/status` | Monitor status |
| `POST` | `/admin/smb-watch/config` | Update configuration |
Logs (Admin Access)
| Method | Endpoint | Description |
| `GET` | `/logs` | Get server log lines |
| `GET` | `/logs/export` | Export logs for date range |
Email (Admin Access)
| Method | Endpoint | Description |
| `GET` | `/email/status` | Email service status |
| `GET` | `/email/log` | Paginated email log |
| `GET` | `/email/log/stats` | Email statistics |
| `GET` | `/email/log/{log_id}` | Single email log entry |
Settings (Admin Write)
| Method | Endpoint | Description |
| `PUT` | `/settings/ticket-defaults` | Update ticket defaults |
Signatures (Admin)
| Method | Endpoint | Description |
| `POST` | `/admin/send-signature-reminders` | Trigger reminders |
---
6. Customer Endpoints (Customer JWT Required)
These endpoints use a separate customer authentication system.
Customer Profile
| Method | Endpoint | Description |
| `GET` | `/customer/api/me` | Customer profile |
| `POST` | `/customer/api/change-password` | Change password |
| `POST` | `/customer/api/delete-account` | Request deletion |
Customer Tickets
| Method | Endpoint | Description |
| `GET` | `/customer/api/service-tickets` | List service tickets |
| `GET` | `/customer/api/support-tickets` | List support tickets |
Customer Devices
| Method | Endpoint | Description |
| `POST` | `/customer/api/devices/register` | Register for push |
| `DELETE` | `/customer/api/devices/{token}` | Unregister |
| `GET` | `/customer/api/devices` | List devices |
Customer Notifications
| Method | Endpoint | Description |
| `GET` | `/customer/api/notifications` | Get notifications |
| `POST` | `/customer/api/notifications/{id}/read` | Mark as read |
| `POST` | `/customer/api/notifications/read-all` | Mark all read |
| `DELETE` | `/customer/api/notifications/{id}` | Delete |
---
7. Portal HTML Endpoints
These endpoints render server-side HTML pages (not JSON APIs).
Customer Portal
| Method | Path | Description |
| `GET` | `/login` | Unified login page |
| `GET` | `/register` | Customer registration |
| `GET` | `/customer/{slug}/` | Customer dashboard |
| `GET` | `/customer/{slug}/ticket/{type}/{number}` | Ticket detail |
| `GET` | `/customer/{slug}/new/service-ticket` | New service ticket form |
| `GET` | `/customer/{slug}/new/support-ticket` | New support ticket form |
| `GET` | `/customer/{slug}/settings` | Account settings |
| `GET` | `/customer/{slug}/notifications` | Notifications page |
| `GET` | `/customer/{slug}/equipment/{id}` | Equipment detail |
Admin Portal
| Method | Path | Description |
| `GET` | `/portal/admin/` | Admin dashboard |
| `GET` | `/portal/admin/ticket/{type}/{number}` | Ticket detail |
| `GET` | `/portal/admin/ticket/{type}/{number}/edit` | Ticket editor |
| `GET` | `/portal/admin/task/{id}` | Task detail |
| `GET` | `/portal/admin/task/{id}/edit` | Task editor |
| `GET` | `/portal/admin/customers` | Customer list |
| `GET` | `/portal/admin/customers/{id}` | Customer detail |
| `GET` | `/portal/admin/vendors` | Vendor list |
| `GET` | `/portal/admin/contacts` | Contact list |
| `GET` | `/portal/admin/equipment` | Equipment list |
| `GET` | `/portal/admin/templates` | Template manager |
| `GET` | `/portal/admin/notifications` | Notifications |
| `GET` | `/portal/admin/settings` | Employee settings |
| `GET` | `/portal/admin/admin-area` | Admin area (admin only) |
Signature Capture
| Method | Path | Description |
| `GET` | `/portal/sign/{token}` | Signature page (token-based, no auth) |
| `POST` | `/portal/sign/{token}` | Submit signature |
---
8. Error Handling
HTTP Status Codes
| Code | Meaning | Example |
| `200` | Success | Successful GET/POST/PUT/PATCH |
| `201` | Created | Resource created (some POST endpoints) |
| `400` | Bad Request | Invalid input, validation error |
| `401` | Unauthorized | Missing or invalid token |
| `403` | Forbidden | User lacks permission |
| `404` | Not Found | Resource doesn't exist |
| `413` | Payload Too Large | Request exceeds size limit |
| `422` | Unprocessable Entity | Pydantic validation failure |
| `429` | Too Many Requests | Rate limit exceeded |
| `500` | Server Error | Unexpected error (check error_id) |
| `503` | Service Unavailable | Database connection failure |
Error Response Format
{
"detail": "Human-readable error message"
}
For 500 errors:
{
"detail": "Internal server error",
"error_id": "a1b2c3d4"
}
Use the error_id to find the corresponding log entry.
Validation Errors (422)
{
"detail": [
{
"loc": ["body", "title"],
"msg": "field required",
"type": "value_error.missing"
}
]
}
---
9. Rate Limiting
Rate Limit Headers
Responses include rate limit information:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1709294400
Default Limits
| Endpoint Type | Max Requests | Window |
| Login (`/token`) | 5 | 5 minutes |
| Registration (`/register`) | 3 | 1 hour |
| General API | 100 | 1 minute |
Rate Limit Exceeded
HTTP 429 Too Many Requests
{"detail": "Rate limit exceeded. Try again later."}
---
10. Pagination
List Endpoints
Most list endpoints support pagination:
GET /tasks/?limit=20&offset=0
GET /customers/list?limit=50&offset=100
GET /notifications?limit=10&offset=0
| Parameter | Default | Description |
| `limit` | 50 | Max items per page |
| `offset` | 0 | Skip first N items |
Response Pattern
List endpoints typically return arrays directly or wrapped objects:
[
{"id": 1, "title": "Task 1", ...},
{"id": 2, "title": "Task 2", ...}
]
---
Previous: Guide 6 — Admin Guide | Next: Guide 8 — JSON Inputs
Corejobtrack Server — JSON Inputs Guide
> Audience: Developers who need to construct valid JSON request bodies for the Corejobtrack API. This guide covers every Pydantic model, field validation rules, and JSONB data structures.
---
Table of Contents
1. Overview
2. User Models
3. Task Models
10. Notification & Read Status
12. Log Models
13. Settings Models
14. Quote Models
15. Lead Models
18. Validation Rules
19. Sample Payloads
---
1. Overview
Content Type
All JSON request bodies must be sent with:
Content-Type: application/json
Null vs. Omitted Fields
Field Length Limits
| Limit | Default | Environment Variable |
| Username | 64 chars | `MAX_USERNAME_LENGTH` |
| Password | 128 chars | `MAX_PASSWORD_LENGTH` |
| Title | 500 chars | `MAX_TITLE_LENGTH` |
| Description | 50,000 chars | `MAX_DESCRIPTION_LENGTH` |
| Note | 100,000 chars | `MAX_NOTE_LENGTH` |
---
2. User Models
Registration (`POST /register`)
{
"username": "jdoe", // Required, max 64 chars
"password": "SecurePass123!", // Required, max 128 chars
"full_name": "John Doe", // Optional
"first_name": "John", // Optional
"last_name": "Doe", // Optional
"nick_name": "JD", // Optional
"email": "[email protected]", // Optional, validated format
"phone": "(555) 234-5678", // Optional
"team": "Service" // Optional
}
Admin Create User (`POST /users/`)
{
"username": "jdoe", // Required
"password": "SecurePass123!", // Optional (or use new_password)
"new_password": "SecurePass123!", // Optional (alternative to password)
"full_name": "John Doe",
"first_name": "John",
"last_name": "Doe",
"nick_name": "JD",
"email": "[email protected]",
"phone": "(555) 234-5678",
"team": "Service"
}
Admin Update User (`PUT /users/{id}`)
{
"username": "jdoe_updated", // Optional
"full_name": "John A. Doe", // Optional
"first_name": "John", // Optional
"last_name": "Doe", // Optional
"nick_name": "Johnny", // Optional
"email": "[email protected]", // Optional, validated
"phone": "(555) 234-5678", // Optional
"team": "Management", // Optional
"role": "admin", // Optional: "pending", "user", "admin"
"new_password": "NewSecurePass!", // Optional
"hide_from_time_entries": false, // Optional: hide from time dropdowns
"hidden_from_sharing": false // Optional: hide from assignment dropdowns
}
Self-Update Profile (`PUT /me`)
{
"full_name": "John Doe", // Optional
"first_name": "John", // Optional
"last_name": "Doe", // Optional
"nick_name": "JD", // Optional
"email": "[email protected]", // Optional, validated
"phone": "(555) 234-5678", // Optional
"team": "Service", // Optional
"chat_color": "#FF5733", // Optional: color for chat messages
"current_password": "OldPass123!", // Required if changing password
"new_password": "NewPass456!" // Optional, max 128 chars
}
---
3. Task Models
Create Task (`POST /tasks/`)
{
"title": "Replace drum unit", // Required, max 500 chars
"description": "Customer reports streaking", // Optional, max 50,000 chars
"status": "open", // Default: "open"
"visibility": "private", // Default: "private" or "public"
"task_type": "Repair", // Optional
"is_private": false, // Optional
"color": "#4A90D9", // Optional: hex color
"template_id": 3, // Optional: create from template
"urgency": "Medium", // Default: "Medium"
"due_date": "2026-03-15", // Optional: ISO date
"task_code": "TSK-001", // Optional: custom code
// Assignment
"assigned_to": 5, // Optional: single user ID
"assigned_team": "Service", // Optional: single team
"assigned_user_ids": [5, 8], // Optional: multiple user IDs
"assigned_teams": ["Service", "Support"], // Optional: multiple teams
// Customer (optional)
"customer_name": "Acme Corp",
"customer_number": "CUST001",
"customer_address_1": "123 Main St",
"customer_address_2": "Suite 200",
"city": "Anytown",
"state": "ST",
"zip": "12345",
// Contact (optional)
"customer_contact_first_name": "Jane",
"customer_contact_last_name": "Doe",
"customer_contact_full_name": "Jane Doe",
"customer_phone": "(555) 123-4567",
"customer_email": "[email protected]",
"customer_contact_preference": "email",
// Equipment (optional)
"equipment": [
{
"make": "HP",
"model": "LaserJet Pro",
"serial": "ABC123",
"count": 1,
"problem": "Streaking",
"problem_description": "Lines across every page"
}
]
}
Update Task (`PATCH /tasks/{id}`)
Only include the fields you want to change:
{
"title": "Updated title",
"status": "in_progress",
"urgency": "High",
"due_date": "2026-03-20",
"assigned_user_ids": [5, 8, 12],
"private_notes": "Internal note about this task"
}
---
4. Service Ticket Models
Create Service Ticket (`POST /service-tickets/`)
{
// Required
"customer_name": "Acme Corp",
// Optional metadata
"title": "PM Visit - Q1 2026",
"description": "Quarterly preventive maintenance",
"status": "Pending", // Default: "Pending"
"urgency": "Medium", // Default: "Medium"
"visibility": "private",
"is_private": false,
"task_type": "Maintenance",
"color": "#4A90D9",
"due_date": "2026-03-15",
"task_code": "SVC-001",
// Assignment
"assigned_to": 5,
"assigned_team": "Service",
"assigned_user_ids": [5, 8],
"assigned_teams": ["Service"],
// Customer & Contact
"customer_number": "CUST001",
"customer_address_1": "123 Main St",
"customer_address_2": "",
"city": "Anytown",
"state": "ST",
"zip": "12345",
"customer_contact_first_name": "Jane",
"customer_contact_last_name": "Doe",
"customer_contact_full_name": "Jane Doe",
"customer_phone": "(555) 123-4567",
"customer_email": "[email protected]",
"customer_contact_preference": "phone",
// Dispatch
"dispatch_type": "Scheduled", // Emergency, Scheduled, PM
"dispatch_number": "D-2026-001",
"dispatch_status": "Dispatched",
"customer_po": "PO-12345",
// Equipment (JSONB array)
"equipment": [
{
"make": "Konica Minolta",
"model": "bizhub C300i",
"serial": "A9K123456",
"count": 1,
"problem": "PM Due",
"problem_description": "Quarterly maintenance scheduled"
}
],
// Problem
"problem_description": "Customer reports paper jams and streaking",
// Time tracking (JSONB array)
"session_timestamps": [
{
"start_time": "2026-03-01T09:00:00",
"stop_time": "2026-03-01T12:30:00",
"time_amount": 3.5, // Hours
"time_type": "regular", // regular, overtime, travel
"num_techs": 1,
"lunch": 0, // Minutes
"services_performed": "Cleaned and calibrated",
"notes": "Used new drum unit",
"ticketed_user_ids": [5],
"start": "09:00",
"end": "12:30",
"duration_minutes": 210
}
],
"total_time_minutes": 210,
// Work performed
"services_performed": "Cleaned rollers, replaced drum, calibrated",
"settings_recorded": ["Color profile", "Paper tray alignment"],
"tasks_completed": ["Clean rollers", "Replace drum", "Run calibration"],
// Resolution
"resolved": true,
"resolution": "All issues resolved during visit",
// Service flags
"liftgate": false,
"sprinter": true,
"remote": false,
"phone_call": false,
// Notes
"public_notes": "Customer visible notes",
"private_notes": "Internal team notes"
}
Update Service Ticket (`PATCH /service-tickets/{id}`)
Same fields as create, all optional. Only send what changed.
---
5. Support Ticket Models
Create Support Ticket (`POST /support-tickets/`)
{
// Required
"customer_name": "Acme Corp",
// Optional metadata
"title": "Cannot print from accounting app",
"description": "Customer reports print jobs queue but never print",
"status": "Pending",
"urgency": "Medium",
"due_date": "2026-03-10",
"visibility": "private",
"is_private": true,
"task_type": "Support",
"color": "#E67E22",
"task_code": "SUP-001",
// Assignment
"assigned_to": 8,
"assigned_team": "Support",
"assigned_user_ids": [8],
"assigned_teams": ["Support"],
// Customer
"customer_number": "CUST001",
"customer_address_1": "123 Main St",
"city": "Anytown",
"state": "ST",
"zip": "12345",
// Contact
"customer_contact_first_name": "Jane",
"customer_contact_last_name": "Doe",
"customer_phone": "(555) 123-4567",
"customer_email": "[email protected]",
// Vendor tracking
"ticket_id": "VENDOR-2026-001",
"vendor_ticket_number": "VT-789",
"ticket_status": "New", // New, Open, Escalated, Resolved
"ticket_type": "Software Issue",
"customer_po": "PO-12345",
// Equipment
"equipment": [
{
"make": "HP",
"model": "LaserJet Pro M428",
"serial": "CNBJ1234",
"count": 1
}
],
// Communication log (JSONB array)
"communication_log": [
{
"timestamp": "2026-03-01T09:00:00Z",
"type": "phone",
"summary": "Customer called about print issue",
"details": "Cannot print from accounting software since update",
"user_id": 8
}
],
// Notes
"private_notes": "May need to escalate to vendor",
"public_notes": "Working on resolution",
// Time
"total_time": 1.5,
// Resolution
"resolved": false,
"resolution": null
}
Add Communication Message (`POST /support-tickets/{id}/communication`)
{
"message": "I've tested the connection and identified the issue. The print driver needs updating.",
"time_spent": 0.5, // Hours spent on this message/action
"is_customer": false, // true if customer sent this
"author_user_id": null // Auto-detected from token
}
---
6. Customer & Contact Models
Create/Update Customer (`POST/PATCH /customers/{id}`)
{
"company": "Acme Corporation",
"contact_id": "CUST001",
"address_1": "123 Main Street",
"address_2": "Suite 200",
"city": "Anytown",
"state": "ST",
"zip": "12345",
"phone": "(555) 123-4567",
"email": "[email protected]",
"account_status": "active"
}
Create Contact (`POST /customers/{id}/contacts`)
{
"first_name": "Jane",
"last_name": "Doe",
"phone": "(555) 234-5678",
"email": "[email protected]",
"is_primary": true
}
Reorder Contacts (`POST /customers/{id}/contacts/reorder`)
{
"contact_ids": [5, 3, 8, 1]
}
---
7. Equipment Models
Create Equipment (`POST /customers/{id}/equipment`)
{
"make": "HP",
"model": "LaserJet Pro M428fdn",
"serial": "CNBJ123456",
"count": 1,
"notes": "Located in 2nd floor copy room"
}
Log Equipment Count (`POST /customer-equipment/{id}/counts`)
{
"count": 45230, // Current meter reading
"notes": "Monthly meter reading" // Optional
}
---
8. Template Models
Create/Update Template (`POST /templates/`, `PUT /templates/{id}`)
{
"name": "Quarterly PM", // Required: template name
"title": "PM Visit - Q1", // Optional: default task title
"description": "Quarterly preventive maintenance visit",
"steps": [ // Required: at least one step
{
"step_description": "Clean all components",
"is_completed": false,
"order_num": 1
},
{
"step_description": "Replace consumable parts",
"is_completed": false,
"order_num": 2
},
{
"step_description": "Run calibration",
"is_completed": false,
"order_num": 3
},
{
"step_description": "Test print quality",
"is_completed": false,
"order_num": 4
}
],
"color": "#27AE60", // Optional: hex color
"task_type": "Maintenance", // Optional
"due_date_offset": 7, // Optional: days from creation
"assigned_user_ids": [5], // Optional
"assigned_teams": ["Service"] // Optional
}
---
9. Signature Models
Submit Signature (Employee) (`POST /service-tickets/{id}/sign`)
{
"signature_data": "data:image/png;base64,iVBORw0KGgo...", // Base64 PNG
"signature_name": "Jane Doe", // Printed name
"send_summary_email": true, // Email PDF to customer
"safety_covers_ok": true, // Safety cover check
"safety_covers_notes": null // Optional notes
}
Submit Signature (Portal) (`POST /portal/sign/{token}`)
{
"signature_data": "data:image/png;base64,iVBORw0KGgo...",
"signature_name": "Jane Doe",
"safety_covers_ok": true,
"safety_covers_notes": "All covers in place"
}
Decline Signature (`POST /service-tickets/{id}/decline-signature`)
{
"decline_reason": "Contact not around", // Required, from valid set
"send_summary_email": false,
"send_signature_request": true // Send post-visit email
}
Valid decline_reason values:
---
10. Notification & Read Status
Mark Sections Read (`POST /tickets/{type}/{id}/mark-read`)
{
"sections": ["details", "notes", "messages", "attachments"]
}
Valid section names vary by entity type.
---
11. Device Token Models
Register Device (`POST /devices/register`)
{
"token": "apns_device_token_string", // Required: from APNs
"platform": "ios", // Default: "ios"
"device_name": "iPhone 15 Pro", // Optional
"app_version": "2.1.0" // Optional
}
Customer Register Device (`POST /customer/api/devices/register`)
Same format as employee device registration.
---
12. Log Models
Single Client Log (`POST /logs`)
{
"message": "App launched successfully", // Required
"level": "info", // Default: "info"
"event_type": "app_launch", // Optional
"context": { // Optional: arbitrary data
"screen": "dashboard",
"connection": "wifi"
},
"client_version": "2.1.0", // Optional
"platform": "ios", // Optional
"device_id": "device-uuid-123", // Optional
"session_id": "session-uuid-456", // Optional
"user": "jdoe", // Optional
"occurred_at": "2026-03-01T09:00:00Z" // Optional
}
Batch Client Logs (`POST /logs/batch`)
{
"entries": [
{
"message": "App launched",
"level": "info",
"event_type": "app_launch"
},
{
"message": "Network timeout on /tasks",
"level": "warning",
"event_type": "network_error",
"context": {"endpoint": "/tasks/", "timeout_ms": 30000}
},
{
"message": "Crash in TaskDetailView",
"level": "error",
"event_type": "crash",
"context": {"stack_trace": "..."}
}
],
"client_version": "2.1.0", // Applies to all entries
"platform": "ios",
"device_id": "device-uuid-123",
"session_id": "session-uuid-456"
}
---
13. Settings Models
Ticket Color Defaults (`PUT /settings/ticket-defaults`)
{
"service_ticket": "#4A90D9", // Optional: hex color
"support_ticket": "#E67E22" // Optional: hex color
}
Dropdown Choices (`PUT /settings/dropdown-choices/{field_name}`)
[
{"value": "Emergency", "label": "Emergency"},
{"value": "Scheduled", "label": "Scheduled"},
{"value": "PM", "label": "Preventive Maintenance"}
]
Task Type (`POST /task_types/`)
{
"name": "Installation"
}
---
14. Quote Models
Create Quote (`POST /quotes`)
{
"customer_id": 42, // Required
"contact_person_id": 15, // Optional
"summary": "New copier installation proposal", // Optional
"sales_lead_id": 7, // Optional: linked lead
// Pricing
"shipped_direct": false,
"labor_warranty": 12, // Months
"account_rep": "John Smith",
"condense_pricing": false,
"lease_enabled": true,
"include_sales_tax": true,
"tax_rate": 7.5,
"quote_term": 60, // Months
"display_terms": "60 Month FMV Lease",
"sale_closed": false,
"monthly_pricing": true,
// Service costs
"rigging_price": 500.00,
"rigging_cost": 300.00,
"delivery_price": 250.00,
"delivery_cost": 150.00,
"installation_price": 1000.00,
"installation_cost": 600.00,
"training_price": 500.00,
"training_cost": 200.00,
"shipping_price": 800.00,
"shipping_cost": 600.00,
"parts_cost": 50.00,
// Feature toggles
"rigging_enabled": true,
"rigging_description": "Crane and dock work",
"installation_enabled": true,
"installation_description": "Full installation with network setup",
"setup_enabled": false,
"training_enabled": true,
"training_description": "2-hour on-site training",
// Service agreements
"sa1_title": "Standard Service Agreement",
"sa1_price": 150.00,
"sa1_emergency": 4,
"sa1_pm": 4,
"sa1_blade_changes": 0,
"sa1_acceleration": 0.0,
"sa1_parts_discount": 10.0,
"sa1_service_discount": 0.0,
// Trade-in
"trade_enabled": true,
"trade_condition": "Good",
"trade_option": "Trade-In",
"trade_moving_costs": 200.00,
"trade_items": [
{
"sort_order": 1,
"qty": 1,
"description": "Old Copier Model XYZ",
"value": 500.00
}
],
// Line items
"line_items": [
{
"sort_order": 1,
"qty": 1,
"item_code": "BZ-C300i",
"description": "bizhub C300i Color MFP",
"msrp": 8500.00,
"markup": 0.0,
"discount": 15.0,
"cost": 5200.00
}
],
// Options
"options": [
{
"sort_order": 1,
"enabled": true,
"description": "Fax Kit FK-514",
"product_code": "FK-514",
"price": 350.00,
"cost": 200.00,
"purchased": false
}
],
// Other
"warranty_notes": "1-year parts and labor warranty included",
"freight": "FOB Destination",
"customer_price": 12500.00,
"availability": "2-3 weeks",
"terms": "Net 30",
"notes": "Customer interested in leasing options",
"private_notes": "Margin on this deal is 35%"
}
---
15. Lead Models
Create Lead (`POST /leads`)
{
"lead_id": "LEAD-2026-001", // Optional: custom ID
"contact_id": 42, // Required: customer ID
"address_1": "123 Main St",
"city": "Anytown",
"state": "ST",
"zip": "12345",
"contact_name": "Jane Doe",
"phone_number": "(555) 123-4567",
"contact_email": "[email protected]",
"company_rep": "John Smith",
"lead_rating": "Hot", // Hot, Warm, Cold
"lead_source": "Referral", // Referral, Website, Trade Show
"master_status": "New", // Default: "New"
"lead_for": "Color Copier",
"lead_details": "Looking to replace aging fleet",
"budget": 25000.00,
// Funnel stages
"contacted": true,
"qualified": true,
"created_configuration": false,
"created_quote": false,
"presented_quote": false,
"samples": false,
"demo": false,
"demo_follow_up": false,
"emailed": true,
"lead_log": "3/1: Initial contact via referral from ABC Corp"
}
---
16. Lease Rate Models
Create Lease Rate (`POST /lease-rates`)
{
"leasing_partner": "Partner Finance Corp", // Required, non-empty
"lease_rep_name": "Bob Johnson", // Optional
"lease_rep_phone": "(555) 345-6789", // Optional
"min_price": 5000.00, // Required
"max_price": 50000.00, // Required
"term_length": 60, // Required: months
"lease_rate": 0.0189, // Required: rate factor
"lease_type": "FMV" // Optional: "FTO" or "FMV"
}
---
17. JSONB Data Structures
These structures are stored as JSONB columns in PostgreSQL and appear as arrays/objects in JSON request/response bodies.
Equipment Array
Used in tasks, service tickets, and support tickets:
[
{
"make": "HP",
"model": "LaserJet Pro M428fdn",
"serial": "CNBJ123456",
"count": 1,
"problem": "Paper jam",
"problem_description": "Jams occur when printing from tray 2"
},
{
"make": "Konica Minolta",
"model": "bizhub C300i",
"serial": "A9K789012",
"count": 1,
"problem": "Streaking",
"problem_description": "Cyan streaks on every page"
}
]
Session Timestamps Array
Time entries on service tickets:
[
{
"start_time": "2026-03-01T09:00:00",
"stop_time": "2026-03-01T12:30:00",
"time_amount": 3.5,
"time_type": "regular",
"num_techs": 2,
"lunch": 30,
"services_performed": "PM cleaning, roller replacement",
"notes": "Used parts from van stock",
"ticketed_user_ids": [5, 8],
"start": "09:00",
"end": "12:30",
"duration_minutes": 210
}
]
Important notes:
Communication Log Array
Messages on support tickets:
[
{
"timestamp": "2026-03-01T09:00:00Z",
"type": "phone",
"summary": "Customer called about print issue",
"details": "Cannot print from accounting software since Windows update",
"user_id": 8
},
{
"timestamp": "2026-03-01T10:30:00Z",
"type": "remote",
"summary": "Remote session - updated print driver",
"details": "Connected via remote desktop, updated driver to v3.2.1",
"user_id": 8
}
]
Change Log (Audit Trail)
Automatically generated, not user-input:
[
{
"timestamp": "2026-03-01T09:00:00Z",
"action": "created",
"user_id": 5,
"details": "Ticket created"
},
{
"timestamp": "2026-03-01T14:00:00Z",
"action": "updated",
"user_id": 5,
"details": "Status changed from 'open' to 'in_progress'"
}
]
Settings Recorded / Tasks Completed
Simple string arrays on service tickets:
{
"settings_recorded": [
"Color calibration: Standard",
"Paper tray 1: Letter",
"Paper tray 2: Legal",
"Duplex: Enabled"
],
"tasks_completed": [
"Cleaned corona wires",
"Replaced separation roller",
"Updated firmware to v2.5",
"Ran test prints (50 pages)"
]
}
---
18. Validation Rules
Email Validation
Emails must match: ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
Password Requirements
When REQUIRE_PASSWORD_COMPLEXITY=true:
Lease Type Validation
Must be exactly "FTO" or "FMV" (case-insensitive, auto-uppercased).
Decline Reason Validation
Must be one of the exact strings:
Status Values
Tasks: open, in_progress, paused, completed, archived, trash
Service Tickets: Pending, open, in_progress, completed, archived, trash
Support Tickets: Pending, open, in_progress, completed, archived, trash
Ticket Status (Support): New, Open, Escalated, Resolved
User Roles: pending, user, admin
---
19. Sample Payloads
Complete Workflow: Create Customer → Service Ticket → Signature
#### Step 1: Create Customer
curl -X POST https://server:8443/customers/ \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{
"company": "Acme Corp",
"contact_id": "ACME001",
"address_1": "123 Main St",
"city": "Anytown",
"state": "ST",
"zip": "12345",
"account_status": "active"
}'
#### Step 2: Add Contact
curl -X POST https://server:8443/customers/1/contacts \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{
"first_name": "Jane",
"last_name": "Doe",
"phone": "(555) 123-4567",
"email": "[email protected]",
"is_primary": true
}'
#### Step 3: Create Service Ticket
curl -X POST https://server:8443/service-tickets/ \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{
"customer_name": "Acme Corp",
"title": "Emergency Service Call",
"description": "Copier completely down",
"urgency": "High",
"dispatch_type": "Emergency",
"equipment": [
{"make": "HP", "model": "LaserJet", "serial": "ABC123", "count": 1, "problem": "Not printing"}
]
}'
#### Step 4: Update with Time Entry
curl -X PATCH https://server:8443/service-tickets/1 \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{
"status": "in_progress",
"session_timestamps": [
{
"start_time": "2026-03-01T09:00:00",
"stop_time": "2026-03-01T11:00:00",
"time_amount": 2.0,
"time_type": "regular",
"num_techs": 1,
"lunch": 0
}
],
"services_performed": "Replaced fuser unit, test prints OK"
}'
#### Step 5: Capture Signature
curl -X POST https://server:8443/service-tickets/1/sign \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{
"signature_data": "data:image/png;base64,iVBORw0KGgo...",
"signature_name": "Jane Doe",
"send_summary_email": true,
"safety_covers_ok": true
}'
#### Step 6: Complete Ticket
curl -X POST https://server:8443/service-tickets/1/complete \
-H "Authorization: Bearer TOKEN"
---
Previous: Guide 7 — API Endpoints | Next: Guide 9 — Database Breakout
Corejobtrack Server — Database Breakout Guide
> Audience: System administrators who want to move the PostgreSQL database to a separate server for performance, security, or scalability reasons.
---
Table of Contents
1. Why Break Out the Database?
4. Step 1: Prepare the Database Server
5. Step 2: Migrate Existing Data
6. Step 3: Configure Network Access
7. Step 4: Update Corejobtrack Configuration
8. Step 5: Test the Connection
10. Connection Pooling Configuration
11. SSL/TLS for Database Connections
12. Backup Strategy
13. Monitoring the Remote Connection
14. Failover & High Availability
16. Troubleshooting
17. Rollback Plan
---
1. Why Break Out the Database?
Running PostgreSQL on a separate server provides several benefits:
| Benefit | Description |
| **Performance** | Database and application don't compete for CPU/RAM |
| **Scalability** | Scale database and application servers independently |
| **Security** | Database server can be isolated on a private network |
| **Backup** | Dedicated backup infrastructure for data |
| **Maintenance** | Database maintenance (vacuuming, upgrades) doesn't affect app |
| **Compliance** | Data residency requirements may dictate separate storage |
When to Consider Breakout
!Placeholder: Before and after architecture diagram
---
2. Architecture Overview
Before (Single Server)
┌───────────────────────────────────┐
│ Single Server │
│ │
│ Corejobtrack App ←→ PostgreSQL │
│ (Port 8443) (Port 5432) │
│ │
│ DB_HOST=localhost │
└───────────────────────────────────┘
After (Separate Servers)
┌──────────────────┐ ┌──────────────────┐
│ App Server │ TCP │ DB Server │
│ │ ──────→ │ │
│ Corejobtrack App │ 5432 │ PostgreSQL │
│ (Port 8443) │ │ (Port 5432) │
│ │ │ │
│ DB_HOST=10.0.0.2│ │ 10.0.0.2 │
└──────────────────┘ └──────────────────┘
│ │
└────── Private Network ─────┘
Key Connection Parameters
Corejobtrack connects to PostgreSQL using these environment variables:
DB_HOST=10.0.0.2 # Database server IP/hostname
DB_PORT=5432 # Database port
DB_NAME=corejobtrack # Database name
DB_USER=corejobtrack_user # Database user
DB_PASSWORD=secret # Database password
---
3. Prerequisites
Database Server
| Requirement | Details |
| OS | Linux (Ubuntu 20.04+, RHEL 8+, etc.) |
| PostgreSQL | 14+ (same or newer version as current) |
| Network | Private network connectivity to app server |
| Firewall | Port 5432 open to app server IP |
| Disk | Sufficient storage for database + growth |
Application Server
| Requirement | Details |
| Network | Can reach database server on port 5432 |
| psycopg2 | Already installed with Corejobtrack |
Optional but Recommended
---
4. Step 1: Prepare the Database Server
Install PostgreSQL
# Ubuntu/Debian
sudo apt update
sudo apt install postgresql postgresql-contrib
# RHEL/CentOS
sudo dnf install postgresql-server postgresql-contrib
sudo postgresql-setup --initdb
sudo systemctl start postgresql
sudo systemctl enable postgresql
Create Database and User
sudo -u postgres psql
-- Create the database user with a strong password
CREATE USER corejobtrack_user WITH PASSWORD 'your_strong_password_here';
-- Create the database
CREATE DATABASE corejobtrack OWNER corejobtrack_user;
-- Grant privileges
GRANT ALL PRIVILEGES ON DATABASE corejobtrack TO corejobtrack_user;
-- Connect and set up extensions
\c corejobtrack
-- Required
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "citext";
-- Optional: for vector search
-- CREATE EXTENSION IF NOT EXISTS vector;
Optimize PostgreSQL Configuration
Edit postgresql.conf for a dedicated database server:
sudo nano /etc/postgresql/16/main/postgresql.conf
Recommended settings for a dedicated DB server:
# Connection Settings
listen_addresses = '*' # Listen on all interfaces
port = 5432
max_connections = 100 # Adjust based on expected load
# Memory (adjust based on available RAM)
shared_buffers = 1GB # 25% of total RAM
effective_cache_size = 3GB # 75% of total RAM
work_mem = 64MB
maintenance_work_mem = 256MB
# WAL Settings
wal_buffers = 64MB
checkpoint_completion_target = 0.9
max_wal_size = 2GB
# Query Planner
random_page_cost = 1.1 # For SSD storage
effective_io_concurrency = 200 # For SSD storage
# Logging
log_min_duration_statement = 1000 # Log queries over 1 second
log_connections = on
log_disconnections = on
Restart PostgreSQL:
sudo systemctl restart postgresql
---
5. Step 2: Migrate Existing Data
Option A: pg_dump / pg_restore (Recommended)
This is the safest method for migrating data.
#### On the Current Server (Source)
# Create a full database dump
pg_dump -h localhost -U corejobtrack_user -d corejobtrack \
--format=custom --file=corejobtrack_backup.dump
# Or plain SQL for debugging
pg_dump -h localhost -U corejobtrack_user -d corejobtrack \
--file=corejobtrack_backup.sql
#### Transfer to New Server
scp corejobtrack_backup.dump [email protected]:/tmp/
#### On the New Database Server
# Restore the dump
pg_restore -h localhost -U corejobtrack_user -d corejobtrack \
--clean --if-exists --no-owner \
/tmp/corejobtrack_backup.dump
# Or for plain SQL:
psql -h localhost -U corejobtrack_user -d corejobtrack < /tmp/corejobtrack_backup.sql
Option B: pg_basebackup (For Large Databases)
For databases larger than 10 GB, use streaming replication for a near-zero-downtime migration:
# On the new server, create a base backup from the old server
pg_basebackup -h old-server -U replication_user \
-D /var/lib/postgresql/16/main \
--wal-method=stream --progress
Verify Data
After migration, verify the data on the new server:
psql -h 10.0.0.2 -U corejobtrack_user -d corejobtrack
-- Check table counts
SELECT 'users' AS table_name, COUNT(*) FROM users
UNION ALL SELECT 'tasks', COUNT(*) FROM tasks
UNION ALL SELECT 'service_tickets', COUNT(*) FROM service_tickets
UNION ALL SELECT 'support_tickets', COUNT(*) FROM support_tickets;
---
6. Step 3: Configure Network Access
On the Database Server
#### Configure pg_hba.conf
Allow the application server to connect:
sudo nano /etc/postgresql/16/main/pg_hba.conf
Add a line for your app server:
# Corejobtrack application server
host corejobtrack corejobtrack_user 10.0.0.1/32 scram-sha-256
# Or for a subnet
host corejobtrack corejobtrack_user 10.0.0.0/24 scram-sha-256
> Security: Use the most specific IP/subnet possible. Never use 0.0.0.0/0 in production.
#### Configure postgresql.conf
Ensure PostgreSQL listens on the correct interface:
listen_addresses = '10.0.0.2' # DB server's private IP
# Or listen on all interfaces:
listen_addresses = '*'
Reload configuration:
sudo systemctl reload postgresql
#### Configure Firewall
# UFW (Ubuntu)
sudo ufw allow from 10.0.0.1 to any port 5432
# firewalld (RHEL/CentOS)
sudo firewall-cmd --permanent --add-rich-rule='
rule family="ipv4"
source address="10.0.0.1/32"
port port="5432" protocol="tcp"
accept'
sudo firewall-cmd --reload
# iptables
sudo iptables -A INPUT -p tcp -s 10.0.0.1 --dport 5432 -j ACCEPT
Test Network Connectivity
From the application server:
# Test TCP connectivity
nc -zv 10.0.0.2 5432
# Test PostgreSQL connectivity
psql -h 10.0.0.2 -U corejobtrack_user -d corejobtrack -c "SELECT 1;"
---
7. Step 4: Update Corejobtrack Configuration
Edit `taskapp.env`
On the application server, update the database connection settings:
# Before (local)
DB_HOST=localhost
# After (remote)
DB_HOST=10.0.0.2
DB_PORT=5432
DB_NAME=corejobtrack
DB_USER=corejobtrack_user
DB_PASSWORD=your_strong_password_here
Adjust Connection Pool Settings
For remote database connections, you may want to adjust the pool:
# Connection pooling
DB_POOL_ENABLED=true
DB_POOL_MIN_CONNECTIONS=2
DB_POOL_MAX_CONNECTIONS=20
# Retry settings (important for network connections)
DB_MAX_RETRIES=5
DB_RETRY_BASE_DELAY=1.0
DB_RETRY_MAX_DELAY=30.0
DB_HEALTH_CHECK_INTERVAL=30
---
8. Step 5: Test the Connection
Test Before Switching
You can test the remote connection without stopping the current server:
# From the app server, test with psql
psql -h 10.0.0.2 -U corejobtrack_user -d corejobtrack -c "
SELECT COUNT(*) AS users FROM users;
SELECT COUNT(*) AS tasks FROM tasks;
SELECT COUNT(*) AS service_tickets FROM service_tickets;
SELECT COUNT(*) AS support_tickets FROM support_tickets;
"
Test with Corejobtrack
Temporarily start a second instance pointing to the remote database:
# Export remote DB config
export DB_HOST=10.0.0.2
export DB_PORT=5432
export DB_NAME=corejobtrack
export DB_USER=corejobtrack_user
export DB_PASSWORD=your_password
# Start test instance on a different port
uvicorn main:app --host 0.0.0.0 --port 9000
Then test:
curl http://localhost:9000/health
curl http://localhost:9000/dbhealth
curl http://localhost:9000/db-status
---
9. Step 6: Switch Over
Planned Switchover Procedure
1. Notify users of brief downtime
2. Stop the application server
sudo systemctl stop taskmanager-ssl
3. Final data sync (if using incremental migration)
# On source server
pg_dump -h localhost -U corejobtrack_user -d corejobtrack \
--format=custom --file=final_backup.dump
scp final_backup.dump [email protected]:/tmp/
# On target server
pg_restore -h localhost -U corejobtrack_user -d corejobtrack \
--clean --if-exists --no-owner /tmp/final_backup.dump
4. Update configuration
# Edit taskapp.env
DB_HOST=10.0.0.2
5. Restart the application server
sudo systemctl start taskmanager-ssl
6. Verify
curl -sk https://localhost:8443/health
curl -sk https://localhost:8443/dbhealth
curl -sk https://localhost:8443/db-status
7. (Optional) Stop local PostgreSQL once confirmed working
sudo systemctl stop postgresql
sudo systemctl disable postgresql
---
10. Connection Pooling Configuration
Built-in Connection Pool
Corejobtrack includes a thread-safe connection pool. Configure it for remote connections:
# Enable pooling (recommended for remote DB)
DB_POOL_ENABLED=true
# Pool size
DB_POOL_MIN_CONNECTIONS=2 # Minimum idle connections
DB_POOL_MAX_CONNECTIONS=20 # Maximum concurrent connections
# Retry on connection failure
DB_MAX_RETRIES=5
DB_RETRY_BASE_DELAY=1.0 # Initial retry delay (seconds)
DB_RETRY_MAX_DELAY=30.0 # Maximum retry delay (seconds)
How the Pool Works
Request → Pool.get_connection()
├── Idle connection available? → Return it
├── Pool not full? → Create new connection → Return it
└── Pool full? → Wait (up to 30s timeout) → Return or 503
↓
Use connection
↓
Pool.return_connection()
├── Connection alive? → Return to pool (after rollback)
└── Connection dead? → Close it
Monitoring Pool Health
curl https://server:8443/db-pool-stats \
-H "Authorization: Bearer ADMIN_TOKEN"
Healthy pool:
{
"available": 5,
"in_use": 2,
"max": 20,
"min": 2
}
Warning signs:
External Connection Pooler (Optional)
For high-traffic deployments, consider pgBouncer:
# Install on the DB server
sudo apt install pgbouncer
# Configure /etc/pgbouncer/pgbouncer.ini
[databases]
corejobtrack = host=localhost port=5432 dbname=corejobtrack
[pgbouncer]
listen_addr = 10.0.0.2
listen_port = 6432
auth_type = scram-sha-256
pool_mode = transaction
max_client_conn = 200
default_pool_size = 25
Then point Corejobtrack to pgBouncer:
DB_HOST=10.0.0.2
DB_PORT=6432 # pgBouncer port
---
11. SSL/TLS for Database Connections
For encrypted database connections over untrusted networks:
On the Database Server
Generate server certificate or use Let's Encrypt:
# Generate self-signed for internal use
openssl req -new -x509 -days 3650 -nodes \
-out /etc/postgresql/16/main/server.crt \
-keyout /etc/postgresql/16/main/server.key \
-subj "/CN=db-server"
chown postgres:postgres /etc/postgresql/16/main/server.*
chmod 600 /etc/postgresql/16/main/server.key
Enable SSL in postgresql.conf:
ssl = on
ssl_cert_file = 'server.crt'
ssl_key_file = 'server.key'
Require SSL in pg_hba.conf:
hostssl corejobtrack corejobtrack_user 10.0.0.0/24 scram-sha-256
On the Application Server
The psycopg2 driver supports SSL parameters. You can set them as environment variables or pass them through the connection configuration.
---
12. Backup Strategy
Automated Backups on the Database Server
Create a backup script:
#!/bin/bash
# /opt/backups/backup_corejobtrack.sh
BACKUP_DIR="/opt/backups/corejobtrack"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
KEEP_DAYS=30
mkdir -p $BACKUP_DIR
# Create compressed backup
pg_dump -U corejobtrack_user -d corejobtrack \
--format=custom --compress=9 \
--file="$BACKUP_DIR/corejobtrack_$TIMESTAMP.dump"
# Remove old backups
find $BACKUP_DIR -name "*.dump" -mtime +$KEEP_DAYS -delete
echo "Backup completed: corejobtrack_$TIMESTAMP.dump"
Schedule with cron:
# Daily at 2 AM
0 2 * * * /opt/backups/backup_corejobtrack.sh >> /var/log/db-backup.log 2>&1
Point-in-Time Recovery (PITR)
For mission-critical deployments, enable WAL archiving:
# postgresql.conf
archive_mode = on
archive_command = 'cp %p /opt/backups/wal/%f'
Backup Verification
Regularly test that backups can be restored:
# Create test database
createdb corejobtrack_test
# Restore
pg_restore -d corejobtrack_test corejobtrack_20260301_020000.dump
# Verify
psql -d corejobtrack_test -c "SELECT COUNT(*) FROM tasks;"
# Clean up
dropdb corejobtrack_test
---
13. Monitoring the Remote Connection
Corejobtrack Built-in Monitoring
Corejobtrack includes a background health monitor that continuously checks database connectivity:
# Health check interval (seconds)
DB_HEALTH_CHECK_INTERVAL=30
The monitor runs every 30 seconds and:
Check DB Status
# Basic health
curl https://server:8443/dbhealth
# Detailed status with metrics
curl https://server:8443/db-status
# Pool statistics
curl https://server:8443/db-pool-stats \
-H "Authorization: Bearer ADMIN_TOKEN"
External Monitoring
Set up alerts for:
| Check | Frequency | Alert Condition |
| `/dbhealth` | 30s | HTTP 503 |
| `/db-status` | 1 min | `consecutive_failures > 3` |
| `/db-pool-stats` | 5 min | `available = 0` |
| DB disk space | 15 min | < 20% free |
| DB connections | 5 min | > 80% of `max_connections` |
PostgreSQL Monitoring on DB Server
# Active connections
psql -c "SELECT count(*) FROM pg_stat_activity WHERE datname='corejobtrack';"
# Long-running queries
psql -c "
SELECT pid, now() - pg_stat_activity.query_start AS duration, query
FROM pg_stat_activity
WHERE datname = 'corejobtrack' AND state != 'idle'
ORDER BY duration DESC;"
# Table sizes
psql -d corejobtrack -c "
SELECT relname AS table, pg_size_pretty(pg_total_relation_size(relid)) AS size
FROM pg_catalog.pg_statio_user_tables
ORDER BY pg_total_relation_size(relid) DESC;"
---
14. Failover & High Availability
PostgreSQL Streaming Replication
For high availability, set up a standby server:
#### On Primary (Master)
# postgresql.conf
wal_level = replica
max_wal_senders = 5
Create replication user:
CREATE ROLE replication_user WITH REPLICATION LOGIN PASSWORD 'repl_password';
#### On Standby (Replica)
pg_basebackup -h primary-server -U replication_user \
-D /var/lib/postgresql/16/main --wal-method=stream
# Create standby.signal
touch /var/lib/postgresql/16/main/standby.signal
# postgresql.conf on standby
primary_conninfo = 'host=primary-server user=replication_user password=repl_password'
Failover Strategy
If the primary database server fails:
1. Promote the standby to primary:
pg_ctl promote -D /var/lib/postgresql/16/main
2. Update Corejobtrack configuration:
DB_HOST=standby-server-ip
3. Restart Corejobtrack:
sudo systemctl restart taskmanager-ssl
Corejobtrack's built-in retry logic will handle brief connection interruptions during failover.
---
15. Performance Tuning
Network Latency
Remote database connections add network latency. Minimize impact:
| Strategy | Implementation |
| Private network | Use 10.x.x.x or 192.168.x.x between servers |
| Same data center | Keep app and DB in the same facility |
| Connection pooling | Enable Corejobtrack's built-in pool |
| Minimize round trips | Corejobtrack already batches queries |
PostgreSQL Tuning for Remote Connections
# Allow more concurrent connections (remote clients may hold longer)
max_connections = 150
# Increase shared buffers for caching
shared_buffers = 2GB
# TCP keepalive (detect broken connections faster)
tcp_keepalives_idle = 60
tcp_keepalives_interval = 10
tcp_keepalives_count = 5
Corejobtrack Pool Tuning
# For remote connections, increase pool size slightly
DB_POOL_MAX_CONNECTIONS=25
# Keep more idle connections warm
DB_POOL_MIN_CONNECTIONS=5
# Faster retry on network blips
DB_RETRY_BASE_DELAY=0.5
DB_MAX_RETRIES=3
---
16. Troubleshooting
Connection Refused
psycopg2.OperationalError: could not connect to server: Connection refused
Check:
1. PostgreSQL is running on the DB server: sudo systemctl status postgresql
2. Listening on correct interface: ss -tlnp | grep 5432
3. Firewall allows the connection: sudo ufw status
4. pg_hba.conf includes the app server IP
5. listen_addresses includes the DB server IP or *
Authentication Failed
FATAL: password authentication failed for user "corejobtrack_user"
Check:
1. Password in taskapp.env matches the PostgreSQL user password
2. Auth method in pg_hba.conf matches (scram-sha-256 vs md5)
3. User exists: \du in psql
Connection Timeout
psycopg2.OperationalError: connection timed out
Check:
1. Network routing: traceroute 10.0.0.2
2. Port open: nc -zv 10.0.0.2 5432
3. No intermediate firewall blocking
4. PostgreSQL is responding: pg_isready -h 10.0.0.2
Pool Exhaustion
HTTP 503: "Database connection pool exhausted"
Fix:
1. Increase DB_POOL_MAX_CONNECTIONS
2. Check for long-running queries on the DB server
3. Monitor with /db-pool-stats
4. Consider pgBouncer for connection multiplexing
Intermittent Disconnections
Corejobtrack's built-in retry logic handles brief disconnections. If they persist:
1. Check network stability between servers
2. Enable TCP keepalives in postgresql.conf
3. Monitor /db-status for consecutive_failures
4. Check PostgreSQL logs for LOG: unexpected EOF on client connection
---
17. Rollback Plan
If the migration fails, you can roll back to the local database quickly:
Before Migration
1. Keep the local PostgreSQL running during the transition period
2. Don't delete or disable local database immediately
3. Keep a copy of the original taskapp.env
Rollback Steps
1. Stop Corejobtrack:
sudo systemctl stop taskmanager-ssl
2. Restore original config:
# Revert to local database
DB_HOST=localhost
3. (Optional) Sync back any new data:
# On remote server
pg_dump -h 10.0.0.2 -U corejobtrack_user -d corejobtrack \
--format=custom --file=rollback_data.dump
# On local server
pg_restore -h localhost -U corejobtrack_user -d corejobtrack \
--clean --if-exists --no-owner rollback_data.dump
4. Restart Corejobtrack:
sudo systemctl start taskmanager-ssl
5. Verify:
curl -sk https://localhost:8443/dbhealth
---
Quick Reference: All Database-Related Environment Variables
| Variable | Default | Description |
| `DB_NAME` | `corejobtrack` | Database name |
| `DB_USER` | (empty) | Database user |
| `DB_PASSWORD` | (empty) | Database password |
| `DB_HOST` | `localhost` | Database server hostname/IP |
| `DB_PORT` | `5432` | Database port |
| `DB_POOL_ENABLED` | `true` | Enable connection pooling |
| `DB_POOL_MIN_CONNECTIONS` | `2` | Minimum pool connections |
| `DB_POOL_MAX_CONNECTIONS` | `20` | Maximum pool connections |
| `DB_MAX_RETRIES` | `5` | Max connection retry attempts |
| `DB_RETRY_BASE_DELAY` | `1.0` | Initial retry delay (seconds) |
| `DB_RETRY_MAX_DELAY` | `30.0` | Maximum retry delay (seconds) |
| `DB_HEALTH_CHECK_INTERVAL` | `30` | Health check interval (seconds) |
---
Previous: Guide 8 — JSON Inputs | Back to: Guide Index