A self-hosted ticket management system for WordPress, built on top of Fluent Support Pro, with custom SLA tracking, B2B portals, analytics dashboards and CSV reporting.
Originally developed as a Final Year Project (TFG) for a real production deployment in an Application Management Services (AMS) consultancy. Now de-branded and published as a learning resource and an alternative to expensive commercial helpdesk platforms.
Most ticket management platforms either:
- Cost a fortune at small scale (Jira Service Management, Zendesk, Freshdesk charge per agent per month).
- Lack the customization power small consultancies actually need (custom statuses, per-company portals, SLA logic mapped to specific working calendars, etc.).
This project sits in the middle: a real, production-tested ticket system that you can deploy on any WordPress install for the cost of a single Fluent Support Pro license, with every line of code under your control.
| Platform | Per-agent fee | Total / year |
|---|---|---|
| Fluent Support Pro + this code | flat fee (unlimited agents) | ~$103 |
| Jira Service Management (Standard) | ~$20 / agent / month | ~$720 |
| Zendesk Support Team | ~$19 / agent / month | ~$684 |
| Zendesk Suite Professional | ~$115 / agent / month | ~$4,140 |
Sources: fluentsupport.com/pricing, atlassian.com/software/jira/service-management/pricing, zendesk.com/pricing. Prices reviewed May 2026; verify current pricing on the vendors' sites before deciding.
The savings grow non-linearly as the team grows: at 10 agents, Jira Standard alone runs at ~$2,400/year while the Fluent Support Pro license stays flat.
- Custom support portal that hides WordPress and presents a clean, branded interface.
- B2B Company Tickets portal: each customer organization sees every ticket from every employee under its domain (with email-domain authorization).
- CSV export of tickets, statuses, histories, messages, work-time metrics and SLA breach excess — directly consumable by Excel.
- Self-service email notifications with an opt-out toggle per customer.
- SLA Analytics dashboard with KPIs, distribution donuts, daily trend chart, top-tagged modules, compliance gauges and a paginated table of SLA breaches.
- Hourly SLA notifier that emails agents at multiple preventive thresholds (3h, 3h30, 6h, 7h, 7h30) before a ticket can breach its SLA.
- Daily summaries at end of business hours: each agent receives the status of their open tickets; customers with overdue replies get a nudge.
- Google Chat integration that pings the team's space the moment a new ticket arrives.
- Per-status time tracking with two parallel scales: business hours (working calendar with holidays + DST) and raw 24/7.
- SLA evaluation computed every 2 minutes via WP-Cron plus live calculation for UI freshness.
- History log for every status transition and tag assignment, exposed back through the CSV.
- Standalone Python auditor to verify, recalculate or migrate metrics outside WordPress.
The customer-facing portal hides every trace of WordPress behind a clean, branded interface:
The B2B Company Tickets portal aggregates every ticket of the customer's organization, with email-domain authorization, filtering, search and pagination:
The SLA Analytics dashboard centralizes the team's operational view — KPIs, status/priority distributions, daily trend, top-tagged modules, compliance gauges and a paginated table of SLA breaches:
The agent panel is the standard Fluent Support workspace, augmented with the custom statuses ("Pending", "In Progress"), preserved status history and SLA metrics:
The CSV exporter generates a structured download per company with the full ticket history, status timeline, conversation log and SLA metrics, ready for Excel:
The WordPress plugins this project relies on, installed on the original deployment:
┌────────────────────────────────────────────────────────────────┐
│ WordPress + Fluent Support Pro │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Engine (snippets/core/) │ │
│ │ ticket-time-manager.php ← counts time per status │ │
│ │ ticket-status-config.php ← custom statuses + REST fix │ │
│ │ tag-category-hook.php ← tag history log │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────┼──────────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Customer│ │ Internal │ │ Notification│ │
│ │ portals │ │ UI │ │ channels │ │
│ │ │ │ │ │ │ │
│ │ company-│ │ sla- │ │ sla- │ │
│ │ tickets │ │ dashboard │ │ notifier │ │
│ │ unified-│ │ │ │ │ │
│ │ header │ │ │ │ google-chat │ │
│ └─────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ MySQL │ │
│ │ wp_fs_tickets, wp_fs_activities (Fluent Support) │ │
│ │ wp_fs_ticket_metrics (this project — schema.sql) │ │
│ │ TimeAdministration (this project — schema.sql) │ │
│ └──────────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────────┘
│
▼
CSV export ──► tools/auditor.py (audit / migrate)
Required:
| Component | Why | Cost |
|---|---|---|
| WordPress 6.x | Host application | Free |
| Fluent Support Pro | Ticket plugin this project extends | from ~$103/year — Fluent Support Pro is essential, the free version doesn't expose the hooks this code relies on |
| FluentSnippets | Runs the PHP snippets without making them into a full plugin | Free |
| FluentSMTP | Reliable email delivery for SLA notifier and webhook | Free |
| MySQL 5.7+ / MariaDB 10.3+ | Database for custom tables and stored procedure | Free |
Optional:
- A managed WordPress host (the original deployment ran on Cloudways/DigitalOcean — any modern stack works).
- Python 3.9+ for the standalone auditor (
tools/auditor.py). - A Google Chat space (for the webhook integration in
extras/google-chat-webhook.php).
mysql -u your_user -p your_database < database/schema.sqlThis creates two tables (TimeAdministration, wp_fs_ticket_metrics) and the stored procedure sp_sync_tickets(). Read the warning at the top of schema.sql — it DROPS the tables if they exist.
Standard WordPress plugin installation. Configure Fluent Support Pro to receive tickets from your support email address (Fluent Support → Settings → Email Piping).
In FluentSnippets, create one snippet per file and paste its contents. Load order matters for the core:
snippets/core/ticket-time-manager.phpsnippets/core/ticket-status-config.phpsnippets/core/tag-category-hook.php
Each file in snippets/extras/ is independent. Install whichever ones you need:
| File | What it does |
|---|---|
company-tickets.php |
B2B portal at /company-tickets |
sla-dashboard.php |
Internal dashboard at /dashboard |
sla-notifier.php |
Hourly email alerts + daily summaries |
export-tickets.php |
"Export CSV" button on the B2B portal |
unified-header.php |
Custom site navigation (requires theme contract — see file header) |
hide-wordpress-admin-bar.php |
Hides WP admin bar for customers |
google-chat-webhook.php |
Pings Google Chat on new ticket |
scheduled-ticket-sync.php |
Daily WP-Cron job that calls sp_sync_tickets() |
add-excel-as-supported-format.php |
Allows .xlsx attachments on tickets |
cd tools/
python auditor.py
# Interactive — prompts for the CSV file exported from the B2B portal.The auditor generates:
<file>_fixed.csv— corrected CSV with recomputed metric columns.TimeAdministration.csv,wp_fs_ticket_metrics.csv— dumps compatible with the SQL tables.update_metrics.sql—INSERT … ON DUPLICATE KEY UPDATEscript to sync the tables.
The code ships with the original deployment's defaults (Madrid timezone, 9–17 business hours, Spanish holiday calendar, 4h/8h SLAs). Every value that is deployment-specific is tagged with an EXAMPLE comment in the source so you can locate and override them quickly.
Quick checklist of what you'll likely want to tune:
| Concern | File(s) | What to look for |
|---|---|---|
| Timezone | snippets/core/ticket-time-manager.php, snippets/extras/sla-notifier.php, snippets/extras/sla-dashboard.php, snippets/extras/export-tickets.php, tools/auditor.py |
Replace 'Europe/Madrid' with your IANA timezone |
| Business hours | snippets/core/ticket-time-manager.php (calc_working_seconds()), tools/auditor.py (working_time_between()) |
Replace 9:00–17:00 boundaries |
| Public holidays | snippets/core/ticket-time-manager.php (get_holidays()), tools/auditor.py (HOLIDAYS set) |
Replace $fixed / $shiftable arrays + Easter logic |
| SLA thresholds | snippets/core/ticket-time-manager.php (SLA_RESPONSE_SECS, SLA_RESOLUTION_SECS), snippets/extras/sla-notifier.php (preventive thresholds), snippets/extras/sla-dashboard.php (UI labels), tools/auditor.py (SLA_LIMIT_*) |
Update both engine constants + display labels + preventive thresholds |
| Customer domain whitelist | snippets/extras/company-tickets.php ($companies), snippets/extras/export-tickets.php ($companies) |
Replace example_company block with your actual customers |
| Customer role | snippets/extras/unified-header.php, snippets/extras/hide-wordpress-admin-bar.php |
Replace 'subscriber' with your WP role for customers |
| Google Chat webhook URL | snippets/extras/google-chat-webhook.php |
Paste your space's incoming-webhook URL |
| Custom statuses & colors | snippets/core/ticket-status-config.php |
Replace pending / in progress slugs and CSS palette |
All EXAMPLE markers point to their counterparts so when you change a value in one file, the related ones are easy to find.
ticket-software/
├── README.md
├── LICENSE
├── .gitignore
│
├── snippets/ # PHP code that lives in WordPress (FluentSnippets)
│ ├── core/ # ⚠️ Load first — required for the system to function
│ │ ├── ticket-time-manager.php # Time tracking + SLA engine
│ │ ├── ticket-status-config.php # Custom statuses + REST overrides
│ │ └── tag-category-hook.php # Tag assignment history log
│ │
│ └── extras/ # Optional add-ons (load only the ones you need)
│ ├── add-excel-as-supported-format.php # Allow .xlsx attachments
│ ├── company-tickets.php # B2B portal
│ ├── export-tickets.php # CSV exporter
│ ├── google-chat-webhook.php # Google Chat new-ticket ping
│ ├── hide-wordpress-admin-bar.php # UI cleanup for customers
│ ├── scheduled-ticket-sync.php # Daily cron for sp_sync_tickets()
│ ├── sla-dashboard.php # Internal analytics dashboard
│ ├── sla-notifier.php # Preventive alerts + daily summaries
│ └── unified-header.php # Custom site navigation
│
├── database/ # MySQL setup (run once)
│ └── schema.sql # Tables + stored procedure
│
└── tools/ # Standalone utilities
└── auditor.py # Metrics auditor and SQL generator
- PHP 7.4+ — running inside WordPress 6.x with Fluent Support Pro hooks.
- JavaScript (ES2020) — vanilla, no build step; loaded inline by FluentSnippets.
- Chart.js 4.4 — pulled from CDN by the SLA Dashboard.
- MySQL — two custom tables + one stored procedure (
sp_sync_tickets). - Python 3.9+ — for the standalone auditor; only
csv,datetime,re,osfrom the standard library. - WP-Cron — for the 2-minute metrics worker and the hourly SLA notifier.
Alejandro Chamorro Mansilla
Originally developed and deployed in 2025–2026 as the Final Year Project (TFG) for the Higher Vocational Degree in Multiplatform Application Development (DAM) at DigitechFP.
The system has been in production use since the project's defense, handling real B2B support tickets for an AMS engagement.
MIT License — see LICENSE for details. You are free to use, modify and distribute this code, including commercially, provided you keep the copyright notice.
Note that this license applies only to the code in this repository. Fluent Support Pro is a third-party commercial plugin (fluentsupport.com) sold under its own terms, and this project does not redistribute it. You need to acquire your own license.
This project was developed for a specific deployment context and then generalized for publication. While every effort has been made to remove deployment-specific values (corporate identifiers, customer data, sensitive URLs, internal credentials) and to mark configurable defaults with EXAMPLE comments, no warranty is offered. Review the code, adapt the configuration to your context, and test thoroughly in a staging environment before any production use. If you find any leftover reference to a specific entity that should not be there, please open an issue.







