Corejobtrack Server — Deployment Guide

Audience: System administrators and DevOps engineers responsible for deploying Corejobtrack Server to production.


Table of Contents

  1. Prerequisites
  2. Server Requirements
  3. PostgreSQL Database Setup
  4. Application Installation
  5. Environment Configuration
  6. SSL Certificate Setup
  7. Systemd Service Configuration
  8. Firewall & Network Configuration
  9. Email (SMTP) Configuration
  10. Apple Push Notifications (APNs) Setup
  11. File Upload Storage
  12. Vector Search / AI Setup (Optional)
  13. SMB Hot Folder (Optional)
  14. Production Checklist
  15. Upgrading & Redeployment
  16. Troubleshooting

1. Prerequisites

Before deploying Corejobtrack Server, ensure you have:

RequirementMinimum VersionNotes
Linux ServerUbuntu 20.04+ / RHEL 8+Deployed on Linux with systemd
Python3.10+3.11 or 3.12 recommended
PostgreSQL14+15 or 16 recommended
Git2.25+For deployment via git pull
OpenSSL1.1+For SSL certificate generation

Required Python Packages

The key dependencies include:

  • FastAPI — Web framework
  • Uvicorn — ASGI server
  • psycopg2-binary or psycopg[binary] — PostgreSQL driver
  • python-jose — JWT token handling
  • bcrypt / passlib — Password hashing
  • Jinja2 — HTML template rendering
  • python-multipart — File upload support
  • httpx — HTTP client for APNs (optional)
  • smbprotocol / smbclient — SMB hot folder support (optional)
  • cryptography — SSL certificate parsing (optional)

Install all dependencies:


pip install -r requirements.txt

2. Server Requirements

Hardware Recommendations

ComponentMinimumRecommended
CPU2 cores4+ cores
RAM2 GB4+ GB
Disk20 GB50+ GB (depends on file uploads)
Network100 Mbps1 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:

  • /opt/taskapp/app/server_cert.pem — Full certificate chain
  • /opt/taskapp/app/server_key.pem — Private key

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:

  • Magic link login — Portal authentication
  • Signature request emails — Post-visit customer signature collection
  • Ticket summary emails — PDF summaries sent to customers
  • Deployment notifications — Admin alerts on server restart
  • Account deletion requests — Notification emails

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:
  • Go to myaccount.google.com > Security > 2-Step Verification > App passwords
  • Select "Mail" and generate
  • Copy the 16-character password to SMTP_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:

  • Automatically generate embeddings for new tasks and tickets
  • Run a background backfill for existing entities
  • Use vector similarity for the /search endpoint

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

  • smbprotocol / smbclient Python package installed
  • Network access to the SMB share

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:

  • [ ] SECRET_KEY is a unique, randomly generated value
  • [ ] SMTP_DEV_MODE is set to false
  • [ ] CORS_ORIGINS lists specific allowed origins (not *)
  • [ ] DB_PASSWORD is a strong, unique password
  • [ ] taskapp.env has restrictive file permissions (chmod 600)
  • [ ] SSL certificates are in place and valid
  • [ ] Firewall allows only required ports
  • [ ] Upload directory exists with correct permissions
  • [ ] Database backups are configured
  • [ ] Log rotation is configured (Corejobtrack uses weekly rotating logs)
  • [ ] Systemd service is enabled for auto-start on boot
  • [ ] Admin user account is created and verified

15. Upgrading & Redeployment

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
  2. Step 1: Environment Setup
  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:

  • A running server with SSL
  • An admin account
  • Company branding configured
  • Email service connected
  • Customer portal accessible
  • Employee accounts created
  • Your first customer and ticket
Placeholder: Onboarding flow diagram

Before You Begin

Make sure you've completed the Deployment Guide. You should have:

  • Corejobtrack Server installed and running
  • PostgreSQL database accessible
  • taskapp.env file created from the sample

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:

  • PDF ticket summaries — Company letterhead
  • Email templates — Header and footer
  • Portal pages — Title and contact information
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:

  • Customer Portal: https://tasks.yourcompany.com:8443/customer/login
  • Admin Portal: https://tasks.yourcompany.com:8443/portal/admin/login
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

RoleAccess LevelDescription
pendingNoneNewly registered, awaiting approval
userStandardCan create/manage tasks and tickets
adminFullAll user permissions + admin panel access

Teams

Teams are text strings that can be assigned to users and used for ticket visibility:

  • Users on the same team can see team-assigned tickets
  • Tickets can be assigned to specific users or teams
  • A user's team is set during account creation or via admin update

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:

TestCommand / ActionExpected Result
Server Healthcurl -k https://localhost:8443/health{"ok": true}
Database Healthcurl -k https://localhost:8443/dbhealth{"db": "ok", ...}
Server Versioncurl -k https://localhost:8443/versionVersion number
Admin LoginPOST /token with admin credentialsAccess token returned
List UsersGET /users/ with admin tokenUser list with your admin
List CustomersGET /customers/list with tokenYour test customer
List TicketsGET /service-tickets/ with tokenYour test ticket
Portal Login PageOpen browser to /customer/loginLogin form renders
Admin PortalOpen browser to /portal/admin/loginAdmin login form
Email TestPOST /email/sendEmail received

Common Issues

ProblemSolution
{"detail": "Not authenticated"}Include Authorization: Bearer TOKEN header
{"detail": "Not authorized"}User needs admin role for this endpoint
Portal returns 404Check PORTAL_BASE_URL is correct
Email not receivedCheck SMTP_ENABLED=true and verify credentials
Database connection timeoutVerify 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:

GuideWhat You'll Learn
Launch & VerifyDetailed server startup, health monitoring, and diagnostics
Customer PortalHow customers interact with the portal
Employee GuideDay-to-day employee workflows
Admin GuideSystem administration and management
API ReferenceComplete API endpoint documentation
JSON InputsRequest/response format reference
Database BreakoutMoving 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
  2. Health Check Endpoints
  3. Startup Sequence
  4. Verifying Core Services
  5. Monitoring & Diagnostics
  6. Common Startup Issues
  7. Stopping & Restarting
  8. Log Files & Rotation

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:

FieldDescription
availableWhether the database is currently reachable
last_checkTimestamp of the last health check
last_errorMost recent error message (null if healthy)
last_recoveryWhen the database last recovered from a failure
consecutive_failuresNumber of consecutive failed checks
total_recoveriesTotal 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"

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:

EndpointIntervalAlert On
GET /health30 secondsNon-200 response
GET /dbhealth60 secondsNon-200 response
GET /db-status5 minutesconsecutive_failures > 3
GET /versionAfter deploymentsVersion 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:

  • Verify DB_HOST, DB_PORT, DB_USER, DB_PASSWORD in taskapp.env
  • Check pg_hba.conf allows connections from the app server
  • Ensure PostgreSQL is listening on the correct interface

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

SettingDefaultDescription
SERVER_LOG_RETENTION_WEEKS52How many weekly log files to keep
CLIENT_LOG_RETENTION_WEEKS52How 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:

PrefixCategoryExample
[AUTH]AuthenticationLogin, token validation, registration
[DB]DatabaseConnection issues, query failures
[EMAIL]Email serviceSend success/failure, SMTP errors
[PORTAL]Customer/admin portalPage loads, form submissions
[ADMIN]Server administrationRestart, deploy, config changes
[SMB]SMB hot folderFile processing, connection issues
[APNS]Push notificationsSend success/failure
[UPLOAD]File uploadsUpload/download operations
[SIGNATURE]Signature systemRequest, capture, reminder
[EMBED]Vector searchEmbedding 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
  4. Your Dashboard
  5. Viewing Tickets
  6. Creating a New Service Ticket
  7. Creating a New Support Ticket
  8. Signing Service Documents
  9. Communicating with the Team
  10. Managing Your Equipment
  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:

  • View your service and support tickets — See the status and history of all work being done for you
  • Create new tickets — Request service visits or open support cases
  • Sign service documents — Digitally sign after a service visit, right from your browser or phone
  • Communicate — Send messages directly to the team working on your tickets
  • Track equipment — See the equipment registered to your account
  • Download PDFs — Get professional summary reports of completed work
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:

  • First Name and Last Name
  • Email Address — This is how you'll log in and receive notifications
  • Password — Must meet security requirements:
  • At least 8 characters
  • Mix of uppercase and lowercase letters
  • Include at least one number
  • Include at least one special character
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"
Placeholder: Login form

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:

ColumnWhat It Means
Ticket #Unique ticket number (e.g., #456789) — click to view details
TitleBrief description of the service request
StatusCurrent state: Open, In Progress, Completed, Archived
SignatureWhether you've signed for the completed service
CreatedWhen the ticket was created

Support Tickets

These represent remote support cases (phone, email, or remote desktop). Each row shows:

ColumnWhat It Means
Ticket #Unique ticket number — click to view details
TitleBrief description of the support request
StatusCurrent state: Open, In Progress, Completed, Archived
CreatedWhen the ticket was created

Status Badges

StatusColorMeaning
OpenBlueNew ticket, not yet started
In ProgressYellowBeing actively worked on
CompletedGreenWork is finished
ArchivedGrayClosed and stored for records
PendingOrangeWaiting for action

Creating New Tickets

Click the "New Ticket" button at the top of the dashboard. You'll be asked to choose between:

  • Service Ticket — For requesting a technician visit
  • Support Ticket — For remote support issues

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

  • Ticket Number and Title
  • Status Badge — Current state of the ticket
  • Signature Status — (Service tickets only) Whether the work has been signed off
  • PDF Export Button — Download a professional PDF summary
  • View History — See the full change log

Customer & Contact Information

  • Your company name and address
  • Contact person, phone, and email
  • Customer PO number (if applicable)

Equipment (Service Tickets)

A table of equipment involved in this service visit:

  • Make, Model, Serial Number
  • Count (quantity)
  • Problem description for each piece of equipment

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:

  • Start and stop times
  • Duration
  • Number of technicians
  • Type of time (regular, overtime, etc.)
  • Total time at the bottom

Attachments

  • Media Gallery — Photos and videos from the service visit
  • Files — Documents like invoices, manuals, or reports (click to download)

Signature Section (Service Tickets)

If the service has been signed:

  • The signature image
  • Name of the person who signed
  • Date and time of signing

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

  • Title — A brief summary of the service request (e.g., "Paper jam in main copier")
  • Description — Detailed explanation of the issue

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

  • Title — Brief summary (e.g., "Cannot connect to network printer")
  • Description — Full details of the problem

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
  1. Enter your full name
  2. Check the safety covers checkbox if applicable
  3. 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):

  • Click "Decline to Sign"
  • Select a reason from the dropdown
  • The team will be notified

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:

  • Who sent it — You or a team member
  • When it was sent — Date and time
  • The message content

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:

  • Make, Model, Serial Number
  • Current count (quantity)
  • Count history — A log of all count changes over time
  • Related tickets — All service visits involving this equipment

11. Notifications

Corejobtrack sends you notifications when important things happen.

Types of Notifications

NotificationWhen It's Sent
Ticket CreatedA new ticket is created for your account
Status ChangedYour ticket's status changes (e.g., to "In Progress")
Message ReceivedA team member replies to your support ticket
Signature RequestedA service visit needs your signature
Account ApprovedYour 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

  • Mark as Read — Click a notification to mark it as read
  • Mark All as Read — Clear all unread indicators at once
  • Delete — Remove notifications you no longer need

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

  • Click on the profile picture area to upload a new photo
  • Supported formats: PNG, JPG, GIF, WebP
  • Maximum size: 5 MB

Personal Information

Update your name and contact details:

  • First Name
  • Last Name
  • Phone (display only in some cases)

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:

  • Minimum 8 characters
  • Must include uppercase and lowercase letters
  • Must include at least one number
  • Must include at least one special character

Email Verification

If your email hasn't been verified:

  • Click "Verify Email"
  • Check your inbox for a verification link
  • Click the link to confirm your email

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:

  • Company letterhead and branding
  • All ticket details (customer info, equipment, time entries)
  • Attached images (resized for print)
  • Signature (if signed)

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.
  • Check your spam/junk folder
  • Make sure the email from Corejobtrack isn't being blocked by your email filters
  • Wait a minute — emails can take a short time to arrive
  • Try requesting a new magic link

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
  6. Working with Tasks
  7. Customer Management
  8. Equipment Tracking
  9. Templates
  10. Attachments & Files
  11. Signatures
  12. Notifications
  13. Search
  14. Your Profile & Settings
  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

FeatureRegular EmployeeAdmin
View assigned tickets & tasksYesYes
Create new tickets & tasksYesYes
Edit tickets & tasksYesYes
View all customersYesYes
Manage customer contactsYesYes
Upload attachmentsYesYes
Capture signaturesYesYes
Generate PDF summariesYesYes
Approve new customer registrationsNoYes
Manage employee accountsNoYes
Access server administrationNoYes
View server logsNoYes
Manage trash/deleted itemsNoYes

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"
  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

The top navigation includes:

TabWhat It Shows
DashboardMain view with all tickets and tasks
NotificationsYour notification center
CustomersCustomer directory
VendorsVendor directory
ContactsCustomer contact list
EquipmentEquipment inventory
TemplatesTask and ticket templates
Admin AreaServer administration (admin role only)

Dashboard Tables

The dashboard displays three tables:

Service Tickets

ColumnDescription
Ticket #Auto-generated number (click to open)
TitleBrief description
CustomerCustomer company name
StatusOpen, In Progress, Completed, Archived
AssignedWho's working on it
CreatedWhen it was created

Support Tickets

ColumnDescription
Ticket #Auto-generated number (click to open)
TitleBrief description
CustomerCustomer company name
StatusCurrent status
CreatedWhen it was created

Tasks

ColumnDescription
Task #Auto-generated task code
TitleTask description
StatusOpen, In Progress, Completed
AssignedWho's responsible
Due DateDeadline

Creating New Items

Click the "New" button to create:

  • Service Ticket — Schedule a service visit
  • Support Ticket — Open a support case
  • Task from Template — Create a task using a pre-built template
  • Blank Task — Create a task from scratch

Pending Customer Approvals

If customers have registered and are awaiting approval, you'll see a Pending Approvals section (admin only) with:

  • Customer name, email, and company
  • Approve and Deny buttons

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:
  • Customer — Select or type the customer name
  • Title — Brief summary
  • Description — Detailed problem description
  1. Optional fields:
  • Equipment — Select affected equipment
  • Problem Description — Per-equipment problem details
  • Urgency — Low, Medium, High, Critical
  • Assigned To — Technician(s) responsible
  • Dispatch Type — Emergency, Scheduled, PM
  • Customer PO — Purchase order number
  1. 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

  • Customer name and address
  • Contact person details

Equipment

  • Table of equipment involved
  • Make, model, serial number
  • Problem description per item

Time Entries

Track time spent on the service visit:

  • Start and stop times for each session
  • Duration (in hours)
  • Number of technicians
  • Time type (regular, overtime, travel)
  • Lunch time deducted
  • Services performed during each session

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
  • Complete — Click the "Complete" button when work is finished
  • Archive — Move completed tickets to archive for long-term storage
  • Trash — Soft-delete (admin can restore)

Generating a PDF Summary

Click the PDF icon to download a professional summary including:

  • Company letterhead
  • All ticket details
  • Equipment list
  • Time entries
  • Attached images
  • Signature (if captured)

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:
  • Customer — Company name
  • Title — Issue summary
  • Description — Full details
  1. Optional fields:
  • Vendor Ticket Number — If escalated to a vendor
  • Ticket Type — Category of support
  • Urgency — Priority level
  • Assigned To — Support agent(s)
  1. 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

  • @Mentions — Type @ to mention a team member. They'll receive a notification.
  • Typing Indicators — See when others are typing
  • Read Receipts — Track who has seen each message
  • Time Tracking — Optionally log time spent per message

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:
  • Title — What needs to be done
  • Description — Details and context
  • Due Date — Deadline
  • Urgency — Priority level
  • Assigned To — Who's responsible
  • Task Type — Category (Installation, Repair, Maintenance, etc.)
  1. Click "Create"

Task Checklist (Steps)

Tasks can have a checklist of steps:

Placeholder: Task with checklist steps
  • Add steps — Click "Add Step" and enter a description
  • Complete steps — Check the checkbox next to each step
  • Reorder steps — Drag steps to rearrange
  • Delete steps — Click the delete icon on a step

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
  • Search by company name, city, or customer code
  • Click a customer to view their full profile

Customer Detail Page

Each customer page shows:

  • Company Information — Name, address, phone, email
  • Contacts — People at the company with their contact details
  • Equipment — Equipment registered to this customer
  • Tickets — All service and support tickets for this customer

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:

  • Add Contact — Click "Add Contact" on the customer detail page
  • Edit Contact — Click the edit icon
  • Delete Contact — Click the delete icon
  • Primary Contact — Mark one contact as primary

Contact Actions

For each contact, you can:

  • Send Password Reset — Sends a reset link to the contact's email
  • Send Magic Link — Sends a login link for portal access
  • Send Verification Email — Sends an email verification link

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:

  • Make, Model, Serial Number
  • Current Count
  • Notes

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:
  • Name — Template name (e.g., "Quarterly PM")
  • Title — Default task title
  • Description — Default task description
  • Steps — Checklist items with order
  • Task Type — Category
  • Color — Visual indicator
  • Due Date Offset — Days from creation to due date
  • Default Assignees — Users or teams
  1. 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

CategoryExtensions
DocumentsPDF, DOC, DOCX, XLS, XLSX, CSV, TXT, RTF, PPT, PPTX
ImagesPNG, JPG, JPEG, GIF, BMP, TIFF, WebP, SVG, HEIC
VideoMP4, MOV, AVI
AudioMP3, WAV
ArchivesZIP, RAR, 7Z
ApplePages, Numbers, Keynote

File Size Limits

  • Maximum file size: 50 MB per file (configurable)
  • Profile pictures: 5 MB maximum

Viewing & Downloading

  • Images — Displayed inline in a media gallery
  • Videos — Playable inline
  • Documents — Click to download

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:
  • Contact not around
  • Contact refused to sign
  • Contact prefers to sign online
  • Other
  1. 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:

  • Unread count — The badge shows how many unread notifications you have
  • Latest notifications — Quick view of recent activity
  • View All — Full notification history

Types of Notifications

EventWho Gets Notified
Ticket createdAssigned users and teams
Ticket status changedCreator, assigned users
Message receivedTicket participants
@MentionedThe mentioned user
Ticket assigned to youYou
Customer registeredAdmins
Signature capturedTicket creator

Managing Notifications

  • Mark as Read — Click a notification
  • Mark All as Read — Clear all unread indicators
  • Delete — Remove individual 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.


Corejobtrack provides a powerful search across all tickets and tasks:

  • Keyword Search — Finds matches in titles, descriptions, customer names, and more
  • Natural Language Search — (If AI search is enabled) Ask questions like "copier jam tickets from last week"

Search from the API:


GET /search?q=paper+jam&entity_types=service_ticket,support_ticket

Each entity type has its own search endpoint:

  • Tasks: GET /tasks/search/{query}
  • Customers: GET /customers/search?query=...
  • Vendors: GET /vendors/search?query=...

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:

  • First Name, Last Name, Nickname
  • Email
  • Phone
  • Team assignment

Profile Picture

  • Upload a profile picture (PNG, JPG, GIF, WebP — max 5 MB)
  • Your picture appears in the navigation and in communication logs

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

  1. Admin Role Overview
  2. Accessing the Admin Area
  3. User Management
  4. Customer Registration Approvals
  5. Server Administration
  6. SSL Certificate Management
  7. Email Service Management
  8. Push Notification (APNs) Management
  9. Database Monitoring
  10. Embedding / AI Search Management
  11. SMB Hot Folder Configuration
  12. Trash & Recovery
  13. Log Management
  14. System Settings
  15. Deployment & Updates
  16. Security Configuration

1. Admin Role Overview

Admin users have full access to all Corejobtrack features plus system administration capabilities. Key admin-only functions:

FunctionDescription
User ManagementCreate, edit, delete employee accounts
Registration ApprovalsApprove/deny customer registrations
Server AdministrationRestart, deploy, view logs
SSL ManagementView/renew SSL certificates
Email ConfigurationMonitor email service status
APNs ConfigurationConfigure push notifications
Database MonitoringView schema status, pool stats
AI/Search ConfigurationManage embedding service
Trash ManagementRestore/permanently delete items
System SettingsConfigure 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';
  1. 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:

  • Database status
  • Server information
  • SSL certificate management
  • Ticket defaults
  • AI/Embedding features
  • Email configuration
  • Signature settings
  • Deploy/Restart controls
  • User management
  • Log viewer
  • Trash recovery

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:

  • username — Change username
  • full_name, first_name, last_name, nick_name — Name fields
  • email, phone — Contact info
  • team — Team assignment
  • rolepending, user, or admin
  • new_password — Reset password
  • hide_from_time_entries — Hide from time entry dropdowns
  • hidden_from_sharing — Hide from assignment dropdowns

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

RoleCan LoginAPI AccessPortal AccessAdmin Area
pendingNoNoNoNo
userYesYesYesNo
adminYesYesYesYes

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

  • The customer receives an approval email
  • Their account becomes active
  • They can log into the customer portal

What Happens on Denial

  • The customer receives a rejection email
  • Their account is removed
  • They can attempt to register again

Managing Contacts

From the Admin Portal, you can also:

  • Send Password Reset — To any customer contact
  • Send Magic Link — Login link without password
  • Send Verification Email — Email address verification

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:

  • days_remaining — Days until expiration
  • expires_soon — True if less than 30 days remain
  • expired — True if the certificate has expired

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:

  • ALL emails are redirected to SMTP_DEV_REDIRECT_ADDRESS
  • The original recipient is shown in the subject line
  • This is for testing — set to false for production

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:

  • Run the SQL statements manually
  • Restart the server (auto-migration will apply them)

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:

  • available: 0 with in_use near max — Pool exhaustion risk
  • Frequent 503 errors — Increase DB_POOL_MAX_CONNECTIONS

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
  }'
FieldDescription
enabledToggle monitoring on/off
pathUNC path (//server/share/) or local path (/path/to/folder)
usernameSMB authentication username
passwordSMB authentication password
file_typeFile format to process (currently only json)
interval_secondsHow 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:

  • log_typeserver or client
  • date_from — Start date (YYYY-MM-DD)
  • date_to — End date (YYYY-MM-DD)

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"
  }'

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:

VariableDefaultDescription
RATE_LIMIT_ENABLEDtrueEnable/disable rate limiting
RATE_LIMIT_LOGIN_MAX5Max login attempts per window
RATE_LIMIT_LOGIN_WINDOW300Window in seconds (5 min)
RATE_LIMIT_REGISTER_MAX3Max registrations per window
RATE_LIMIT_REGISTER_WINDOW3600Window in seconds (1 hour)
RATE_LIMIT_GENERAL_MAX100Max general requests per window
RATE_LIMIT_GENERAL_WINDOW60Window in seconds (1 min)

Account Lockout

VariableDefaultDescription
ACCOUNT_LOCKOUT_ENABLEDtrueEnable/disable lockout
ACCOUNT_LOCKOUT_THRESHOLD10Failures before lockout
ACCOUNT_LOCKOUT_DURATION900Lockout duration (15 min)

CORS Policy

VariableDefaultDescription
CORS_ORIGINS*Allowed origins (comma-separated)
CORS_ALLOW_CREDENTIALStrueAllow 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

VariableDefaultDescription
MIN_PASSWORD_LENGTH8Minimum password length
REQUIRE_PASSWORD_COMPLEXITYtrueRequire 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
  2. Authentication
  3. Public Endpoints (No Auth Required)
  4. Employee Endpoints (JWT Required)
  5. Admin Endpoints (Admin Role Required)
  6. Customer Endpoints (Customer JWT Required)
  7. Portal HTML Endpoints
  8. Error Handling
  9. Rate Limiting
  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:

  • POST /token — Uses application/x-www-form-urlencoded
  • File uploads — Use multipart/form-data

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:

  • Swagger UI: https://your-server:8443/docs
  • ReDoc: https://your-server:8443/redoc

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

  • Default: 720 minutes (12 hours), configurable via ACCESS_TOKEN_EXPIRE_MINUTES
  • Expired tokens return 401 {"detail": "Token has expired"}

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

MethodEndpointDescription
GET/healthBasic health check — returns {"ok": true}
GET/versionServer version — returns {"version": "0.981"}
GET/dbhealthDatabase connectivity check
GET/db-statusDetailed DB status with recovery tracking

Authentication

MethodEndpointDescription
POST/registerEmployee self-registration
POST/tokenEmployee login (returns JWT)

Customer Authentication

MethodEndpointDescription
POST/customer/api/registerCustomer registration (join existing company)
POST/customer/api/loginCustomer login
POST/customer/api/magic-linkRequest magic link email
POST/customer/api/verify-magic-linkVerify magic link token
POST/customer/api/password-resetRequest password reset

Feedback

MethodEndpointDescription
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

MethodEndpointDescription
GET/meGet current user profile with teams
PUT/meUpdate profile (name, email, phone, chat_color)
POST/me/delete-accountRequest account deactivation
POST/me/profile-pictureUpload profile picture
DELETE/me/profile-pictureDelete profile picture
GET/users/{user_id}/profile-pictureGet any user's profile picture
POST/token/refreshRefresh 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

MethodEndpointDescription
GET/users/listPublic user list (for assignment dropdowns)

Tasks

MethodEndpointDescription
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}/pausePause task
POST/tasks/{task_id}/resumeResume task
POST/tasks/{task_id}/archiveArchive task
POST/tasks/{task_id}/completeComplete task
GET/tasks/{task_id}/changelogGet 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)

MethodEndpointDescription
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

MethodEndpointDescription
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

MethodEndpointDescription
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}/archiveArchive ticket
POST/service-tickets/{ticket_id}/completeComplete ticket
POST/service-tickets/{ticket_id}/restoreRestore from archive
GET/service-tickets/{ticket_id}/summary.pdfDownload PDF summary
GET/service-tickets/{ticket_id}/changelogGet 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

MethodEndpointDescription
POST/service-tickets/{ticket_id}/signSubmit signature
POST/service-tickets/{ticket_id}/decline-signatureDecline signature
POST/service-tickets/{ticket_id}/request-signatureSend 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

MethodEndpointDescription
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}/archiveArchive
POST/support-tickets/{ticket_id}/completeComplete
POST/support-tickets/{ticket_id}/restoreRestore from archive
POST/support-tickets/{ticket_id}/communicationAdd communication message
GET/support-tickets/{ticket_id}/comm-readersGet read receipts
GET/support-tickets/{ticket_id}/summary.pdfDownload PDF
GET/support-tickets/{ticket_id}/changelogGet 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

MethodEndpointDescription
GET/customers/listList customers (paginated)
GET/customers/searchSearch 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}/contactsCreate contact
PATCH/customer-contacts/{contact_id}Update contact
DELETE/customer-contacts/{contact_id}Delete contact
POST/customers/{customer_id}/contacts/reorderReorder contacts

GET /customers/search Query Parameters:

  • query — Search term
  • search_field — Field to search (name, contact_id, city)
  • limit, offset — Pagination

Equipment

MethodEndpointDescription
GET/customers/{customer_id}/equipmentList equipment
POST/customers/{customer_id}/equipmentAdd equipment
PATCH/customer-equipment/{equipment_id}Update equipment
DELETE/customer-equipment/{equipment_id}Delete equipment
POST/customer-equipment/{equipment_id}/countsLog count
GET/customer-equipment/{equipment_id}/countsGet count history

POST Equipment:


{
  "make": "HP",
  "model": "LaserJet Pro M428",
  "serial": "CNBJ1234",
  "count": 1,
  "notes": "Located in 2nd floor copy room"
}

Vendors

MethodEndpointDescription
GET/vendors/listList vendors (paginated)
GET/vendors/searchSearch vendors

Attachments

MethodEndpointDescription
POST/attachments/{entity_type}/{entity_id}/uploadUpload 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

MethodEndpointDescription
GET/notificationsGet notifications
POST/notifications/{id}/readMark as read
POST/notifications/read-allMark all as read
DELETE/notifications/{id}Delete notification

GET /notifications Query Parameters:

  • limit — Max results
  • offset — Skip first N
  • unread_only — Boolean filter

Ticket Read Status

MethodEndpointDescription
GET/tickets/{type}/{id}/read-statusGet section read status
POST/tickets/{type}/{id}/mark-readMark sections as read

POST /mark-read:


{
  "sections": ["details", "notes", "messages", "attachments"]
}

Ticket types: task, service_ticket, support_ticket


Typing Indicators

MethodEndpointDescription
POST/tickets/{type}/{id}/typingRecord user typing
GET/tickets/{type}/{id}/typingGet who's typing

Archived Items

MethodEndpointDescription
GET/archived/List archived items
GET/archived/{id}/Get archived item
PATCH/archived/{id}/Update archived item
POST/archived/{id}/restoreRestore to open
DELETE/archived/{id}/Move to trash

MethodEndpointDescription
GET/customers/{customer_id}/related-itemsTickets for customer
GET/equipment/{equipment_id}/related-itemsTickets for equipment

Templates

MethodEndpointDescription
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

MethodEndpointDescription
GET/ticket-number/{number}Find ticket by number

MethodEndpointDescription
GET/searchNatural language / keyword search

Query Parameters:

  • q — Search query
  • entity_types — Comma-separated filter (task, service_ticket, support_ticket)
  • limit — Max results
  • threshold — Similarity threshold (for vector search)

Leads

MethodEndpointDescription
GET/leadsList leads (paginated)
GET/leads/{lead_id}Get single lead
POST/leadsCreate lead
PATCH/leads/{lead_id}Update lead
DELETE/leads/{lead_id}Delete lead

Quotes

MethodEndpointDescription
GET/quotesList quotes
GET/quotes/{quote_id}Get single quote
POST/quotesCreate quote
PATCH/quotes/{quote_id}Update quote
DELETE/quotes/{quote_id}Delete quote

Lease Rates

MethodEndpointDescription
GET/lease-ratesList lease rates
POST/lease-ratesCreate lease rate
PATCH/lease-rates/{id}Update lease rate
DELETE/lease-rates/{id}Delete lease rate

Device Tokens (Push Notifications)

MethodEndpointDescription
POST/devices/registerRegister device for push
DELETE/devices/{token}Unregister device
GET/devicesList registered devices

POST /devices/register:


{
  "token": "device_token_from_apns",
  "platform": "ios",
  "device_name": "iPhone 15 Pro",
  "app_version": "2.1.0"
}

Settings

MethodEndpointDescription
GET/settings/ticket-defaultsGet 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

Email

MethodEndpointDescription
POST/email/sendQueue email for sending

Logs (Client Submission)

MethodEndpointDescription
POST/logsSubmit single client log
POST/logSubmit single client log (alias)
POST/logs/batchSubmit batch of client logs

5. Admin Endpoints (Admin Role Required)

All endpoints require both a valid JWT and is_admin: true.

User Management

MethodEndpointDescription
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-pictureDelete user's picture

Trash Management

MethodEndpointDescription
GET/admin/trashList trashed items
POST/admin/trash/{id}/restoreRestore item
DELETE/admin/trash/{id}Permanently delete
POST/admin/trash/emptyEmpty all trash

Server Administration

MethodEndpointDescription
POST/admin/restartDeploy & restart server
GET/admin/deploy-statusLast deployment details

SSL Certificate

MethodEndpointDescription
GET/admin/ssl/cert-infoCertificate details
POST/admin/ssl/renew-certRegenerate certificate

APNs Management

MethodEndpointDescription
GET/admin/apns/statusAPNs status & stats
GET/admin/apns/configCurrent APNs config
POST/admin/apns/configSave APNs config
POST/admin/apns/testSend test push

Database

MethodEndpointDescription
GET/admin/db/schema-statusSchema diagnostics
GET/db-pool-statsConnection pool stats
GET/security-statusSecurity config status
MethodEndpointDescription
GET/admin/embedding/statusEmbedding service status
POST/admin/embedding/toggleEnable/disable embeddings
POST/admin/embedding/api-keySave OpenAI API key
DELETE/admin/embedding/api-keyRemove API key
POST/admin/embedding/test-keyValidate API key
PUT/admin/embedding/budgetSet budget cap
POST/admin/embedding/reset-usageReset usage counters
GET/admin/embedding/backfill-statusBackfill progress
POST/admin/embedding/backfill-startStart backfill

SMB Watch

MethodEndpointDescription
GET/admin/smb-watch/statusMonitor status
POST/admin/smb-watch/configUpdate configuration

Logs (Admin Access)

MethodEndpointDescription
GET/logsGet server log lines
GET/logs/exportExport logs for date range

Email (Admin Access)

MethodEndpointDescription
GET/email/statusEmail service status
GET/email/logPaginated email log
GET/email/log/statsEmail statistics
GET/email/log/{log_id}Single email log entry

Settings (Admin Write)

MethodEndpointDescription
PUT/settings/ticket-defaultsUpdate ticket defaults

Signatures (Admin)

MethodEndpointDescription
POST/admin/send-signature-remindersTrigger reminders

6. Customer Endpoints (Customer JWT Required)

These endpoints use a separate customer authentication system.

Customer Profile

MethodEndpointDescription
GET/customer/api/meCustomer profile
POST/customer/api/change-passwordChange password
POST/customer/api/delete-accountRequest deletion

Customer Tickets

MethodEndpointDescription
GET/customer/api/service-ticketsList service tickets
GET/customer/api/support-ticketsList support tickets

Customer Devices

MethodEndpointDescription
POST/customer/api/devices/registerRegister for push
DELETE/customer/api/devices/{token}Unregister
GET/customer/api/devicesList devices

Customer Notifications

MethodEndpointDescription
GET/customer/api/notificationsGet notifications
POST/customer/api/notifications/{id}/readMark as read
POST/customer/api/notifications/read-allMark all read
DELETE/customer/api/notifications/{id}Delete

7. Portal HTML Endpoints

These endpoints render server-side HTML pages (not JSON APIs).

Customer Portal

MethodPathDescription
GET/loginUnified login page
GET/registerCustomer registration
GET/customer/{slug}/Customer dashboard
GET/customer/{slug}/ticket/{type}/{number}Ticket detail
GET/customer/{slug}/new/service-ticketNew service ticket form
GET/customer/{slug}/new/support-ticketNew support ticket form
GET/customer/{slug}/settingsAccount settings
GET/customer/{slug}/notificationsNotifications page
GET/customer/{slug}/equipment/{id}Equipment detail

Admin Portal

MethodPathDescription
GET/portal/admin/Admin dashboard
GET/portal/admin/ticket/{type}/{number}Ticket detail
GET/portal/admin/ticket/{type}/{number}/editTicket editor
GET/portal/admin/task/{id}Task detail
GET/portal/admin/task/{id}/editTask editor
GET/portal/admin/customersCustomer list
GET/portal/admin/customers/{id}Customer detail
GET/portal/admin/vendorsVendor list
GET/portal/admin/contactsContact list
GET/portal/admin/equipmentEquipment list
GET/portal/admin/templatesTemplate manager
GET/portal/admin/notificationsNotifications
GET/portal/admin/settingsEmployee settings
GET/portal/admin/admin-areaAdmin area (admin only)

Signature Capture

MethodPathDescription
GET/portal/sign/{token}Signature page (token-based, no auth)
POST/portal/sign/{token}Submit signature

8. Error Handling

HTTP Status Codes

CodeMeaningExample
200SuccessSuccessful GET/POST/PUT/PATCH
201CreatedResource created (some POST endpoints)
400Bad RequestInvalid input, validation error
401UnauthorizedMissing or invalid token
403ForbiddenUser lacks permission
404Not FoundResource doesn't exist
413Payload Too LargeRequest exceeds size limit
422Unprocessable EntityPydantic validation failure
429Too Many RequestsRate limit exceeded
500Server ErrorUnexpected error (check error_id)
503Service UnavailableDatabase 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 TypeMax RequestsWindow
Login (/token)55 minutes
Registration (/register)31 hour
General API1001 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
ParameterDefaultDescription
limit50Max items per page
offset0Skip 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
  4. Service Ticket Models
  5. Support Ticket Models
  6. Customer & Contact Models
  7. Equipment Models
  8. Template Models
  9. Signature Models
  10. Notification & Read Status
  11. Device Token Models
  12. Log Models
  13. Settings Models
  14. Quote Models
  15. Lead Models
  16. Lease Rate Models
  17. JSONB Data Structures
  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

  • Omitted fields use their default values (typically null or empty string)
  • Explicitly null fields ("field": null) are treated the same as omitted
  • On PATCH/PUT updates, only include fields you want to change

Field Length Limits

LimitDefaultEnvironment Variable
Username64 charsMAX_USERNAME_LENGTH
Password128 charsMAX_PASSWORD_LENGTH
Title500 charsMAX_TITLE_LENGTH
Description50,000 charsMAX_DESCRIPTION_LENGTH
Note100,000 charsMAX_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:

  • "Contact not around"
  • "Contact refused to sign"
  • "Contact prefers to sign online"
  • "Other"

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
}

[
  {"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:

  • time_amount is in hours
  • lunch is in minutes
  • duration_minutes is in minutes
  • num_techs multiplied by time_amount gives total person-hours

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:

  • Minimum length: 8 characters (configurable via MIN_PASSWORD_LENGTH)
  • Must contain uppercase letter
  • Must contain lowercase letter
  • Must contain digit
  • Must contain special character

Lease Type Validation

Must be exactly "FTO" or "FMV" (case-insensitive, auto-uppercased).

Decline Reason Validation

Must be one of the exact strings:

  • "Contact not around"
  • "Contact refused to sign"
  • "Contact prefers to sign online"
  • "Other"

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?
  2. Architecture Overview
  3. Prerequisites
  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
  9. Step 6: Switch Over
  10. Connection Pooling Configuration
  11. SSL/TLS for Database Connections
  12. Backup Strategy
  13. Monitoring the Remote Connection
  14. Failover & High Availability
  15. Performance Tuning
  16. Troubleshooting
  17. Rollback Plan

1. Why Break Out the Database?

Running PostgreSQL on a separate server provides several benefits:

BenefitDescription
PerformanceDatabase and application don't compete for CPU/RAM
ScalabilityScale database and application servers independently
SecurityDatabase server can be isolated on a private network
BackupDedicated backup infrastructure for data
MaintenanceDatabase maintenance (vacuuming, upgrades) doesn't affect app
ComplianceData residency requirements may dictate separate storage

When to Consider Breakout

  • Application server CPU usage is high during queries
  • Database size exceeds 10 GB
  • Multiple Corejobtrack instances need to share a database
  • Security policy requires database isolation
  • You need dedicated database backup infrastructure
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

RequirementDetails
OSLinux (Ubuntu 20.04+, RHEL 8+, etc.)
PostgreSQL14+ (same or newer version as current)
NetworkPrivate network connectivity to app server
FirewallPort 5432 open to app server IP
DiskSufficient storage for database + growth

Application Server

RequirementDetails
NetworkCan reach database server on port 5432
psycopg2Already installed with Corejobtrack
  • pgvector extension — If you use AI/vector search
  • SSL certificates — For encrypted database connections
  • pgBouncer — Connection pooling proxy (for high-traffic deployments)

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

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
  1. Stop the application server

   sudo systemctl stop taskmanager-ssl
  1. 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
  1. Update configuration

   # Edit taskapp.env
   DB_HOST=10.0.0.2
  1. Restart the application server

   sudo systemctl start taskmanager-ssl
  1. Verify

   curl -sk https://localhost:8443/health
   curl -sk https://localhost:8443/dbhealth
   curl -sk https://localhost:8443/db-status
  1. (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:

  • available: 0 — All connections are in use
  • in_use near max — Pool is almost exhausted
  • Frequent 503 errors — Increase DB_POOL_MAX_CONNECTIONS

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:

  • Tests the database connection
  • Tracks consecutive failures
  • Records recovery events
  • Updates the status available via /db-status

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:

CheckFrequencyAlert Condition
/dbhealth30sHTTP 503
/db-status1 minconsecutive_failures > 3
/db-pool-stats5 minavailable = 0
DB disk space15 min< 20% free
DB connections5 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
  1. Update Corejobtrack configuration:

   DB_HOST=standby-server-ip
  1. 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:

StrategyImplementation
Private networkUse 10.x.x.x or 192.168.x.x between servers
Same data centerKeep app and DB in the same facility
Connection poolingEnable Corejobtrack's built-in pool
Minimize round tripsCorejobtrack 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
  1. Restore original config:

   # Revert to local database
   DB_HOST=localhost
  1. (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
  1. Restart Corejobtrack:

   sudo systemctl start taskmanager-ssl
  1. Verify:

   curl -sk https://localhost:8443/dbhealth

VariableDefaultDescription
DB_NAMEcorejobtrackDatabase name
DB_USER(empty)Database user
DB_PASSWORD(empty)Database password
DB_HOSTlocalhostDatabase server hostname/IP
DB_PORT5432Database port
DB_POOL_ENABLEDtrueEnable connection pooling
DB_POOL_MIN_CONNECTIONS2Minimum pool connections
DB_POOL_MAX_CONNECTIONS20Maximum pool connections
DB_MAX_RETRIES5Max connection retry attempts
DB_RETRY_BASE_DELAY1.0Initial retry delay (seconds)
DB_RETRY_MAX_DELAY30.0Maximum retry delay (seconds)
DB_HEALTH_CHECK_INTERVAL30Health check interval (seconds)

Previous: Guide 8 — JSON Inputs | Back to: Guide Index