Admin Guide
Configure and manage every aspect of your Go Help Desk installation.
The admin panel is accessible from the sidebar when logged in as an Admin. All configuration in this guide is done through the admin panel unless a restart is required (only for environment variable changes).
Categories, Types & Items
Ticket classification follows a three-level hierarchy: Category → Type → Item. This model, sometimes called CTI (Classification-Type-Item), is borrowed from ITSM tooling like BMC Remedy. It lets you route tickets consistently based on what kind of request they are.
Hierarchy rules
- Every ticket must have a Category. Without at least one Category, tickets cannot be created.
- Types are optional. If a Category has no Types, the Type dropdown is hidden entirely on the ticket form.
- Items are optional. If a Type has no Items, the Item dropdown is hidden.
- Each level filters the next — selecting a Category only shows Types that belong to it; selecting a Type only shows Items under it.
Worked example
Hardware
Laptop
Won't power on
Broken screen
Keyboard issue
Battery not charging
Desktop
Won't power on
Monitor issue
Printer
Not printing
Paper jam
Low ink
Software
Microsoft 365
Outlook not loading
Teams audio issue
SharePoint access
VPN
Can't connect
Slow connection
Custom application
Access Requests
New account
Active Directory
Email / Microsoft 365
VPN
Application access
Permission change
Account unlock
Facilities
Building access
Equipment request
Ergonomics
Managing the hierarchy
Navigate to Admin → Categories. The page shows a tree of all Categories with their Types and Items.
- Add Category — click New Category. Give it a name. It appears immediately in the ticket form.
- Add Type — click Add Type next to a Category. Types cannot exist without a parent Category.
- Add Item — click Add Item next to a Type.
- Reorder — drag-and-drop entries to change the order they appear in the ticket form dropdowns.
- Delete — Categories and Types with existing tickets cannot be deleted. Archive them (hide from new tickets) instead.
Groups in the CTI editor
Each expanded Category or Type row shows a Groups subsection — the groups currently handling tickets at that node. You can add or remove groups directly from here without navigating to the Groups page. See Groups & scope for how scope is calculated. Items do not have a group management section.
Custom fields in the CTI editor
Each expanded Category, Type, or Item row shows a Fields subsection where you assign custom fields to that node. See Custom fields for the full explanation.
Users & roles
Manage accounts from Admin → Users. The page lists all accounts with their role, login type, and join date. Click any row to open the user detail page.
Roles
| Role | Capabilities |
|---|---|
| Admin | Full access to all settings, all tickets, and all user accounts. Can manage categories, groups, statuses, tags, SLA policies, email templates, webhooks, and plugins. Always retains local-auth access even when SAML is enabled. |
| Staff | Can view, reply to, assign, and update tickets within their group scope. Can look up any ticket by ID regardless of scope. Can leave internal notes not visible to users. Can resolve and close tickets. Can add and remove tags on any ticket. Cannot access admin settings. |
| User | Can submit tickets and view their own tickets. Can add replies while the ticket is open. Can reopen a Resolved ticket by replying within the configured reopen window. Cannot see other users' tickets. |
User detail page
Click any user in the list to open their detail page. From there you can:
- Edit profile — change display name, email address, and role. Disabled accounts must be re-enabled before editing profile fields.
- View account info — member since date, login type (Local / SSO / Local + SSO), and MFA enrollment status.
- Reset MFA — clears the user's TOTP secret. The user is prompted to re-enroll on next login. Only shown when the user has MFA active.
- Enable / Disable account — disabled accounts cannot log in. All tickets and history are preserved. Re-enable at any time.
- Reset password — set a new password directly from the admin panel. Only shown for accounts that have a local password (not SSO-only accounts).
- Manage group membership — add the user to or remove them from any group.
- Delete account — permanently removes the account. Tickets and replies are preserved with a "removed user" placeholder. Requires a second confirmation click. Prefer disabling instead when there is any chance the account may need to be re-activated.
Creating a user
Click Add User from the Users list. Enter email, display name, role, and an initial password. The account is created immediately and appears in the list.
Login type column
The Login column in the users list shows how the account authenticates:
- Local — the account uses a password set in Go Help Desk.
- SSO — the account was provisioned through SAML and has no local password.
- Local + SSO — the account can authenticate with either method (common when an admin account was created before SAML was enabled).
Groups & scope
Staff visibility — which tickets a staff member sees in their queue — is controlled entirely by group membership. An admin always sees all tickets. A staff member not in any group sees no tickets (except by direct search by ID).
How scope is calculated
When a staff member loads their ticket queue, the system collects all CTI scope entries from all their groups, unions them, and returns tickets that match any of those entries.
Scope entries are Category/Type pairs:
- Category only (Type = null) — the staff member sees all tickets in that Category, regardless of Type or Item.
- Category + Type — the staff member sees only tickets with that exact Category and Type. Tickets in the same Category with a different Type are not shown.
Items never affect scope. A staff member scoped to Hardware / Laptop sees all laptop tickets regardless of which Item is selected.
Creating and configuring a group
- Go to Admin → Groups → New group.
- Give the group a name (e.g. "Tier 1 Support", "Networking").
- Under Scope, add one or more Category/Type pairs. Click Add scope entry, pick a Category, and optionally narrow to a Type.
- Under Members, add staff accounts.
Common configurations
| Setup | Configuration |
|---|---|
| Single team, all tickets | One group. Add all Categories (no Type). Add all staff as members. |
| Separate teams by category | One group per team. Each group scoped to the categories that team handles. Assign staff to the relevant group(s). |
| Tiered support | Tier 1 group scoped to all Categories. Tier 2 group scoped to specific Category/Type pairs for complex issues. Staff in Tier 2 also see escalated tickets because they can be assigned directly (assignment overrides scope visibility for the assignee). |
| Cross-functional specialists | Staff can belong to multiple groups. A networking specialist is in "Tier 1" (full scope) and "Networking specialists" (scoped to Software / VPN). They see all tickets from Tier 1 scope plus all VPN-specific tickets. |
Assignment vs. scope
Scope determines what a staff member sees in their queue. Assignment is separate — any staff member can be assigned any ticket, even if it's outside their queue scope. Once assigned, the ticket appears in the assignee's queue regardless of CTI.
This is intentional: you should never be unable to escalate a ticket to a specialist because of routing rules.
Custom fields
Custom fields let admins attach structured data to tickets beyond the built-in fields (subject, description, priority, CTI). They are managed from Admin → Custom Fields.
Field definitions
A field definition is the global blueprint for a field. It has:
- Name — unique label shown on forms and the ticket detail page.
- Type — controls what input is rendered:
text— single-line text inputtextarea— multi-line text inputnumber— numeric inputselect— dropdown of predefined options (set on the field definition)
- Options (select only) — comma-separated list of allowed values (e.g. "IT, HR, Finance").
- Sort order — controls display order within a CTI level.
- Active — inactive fields are hidden from new-ticket forms. Existing values are preserved for history. Field definitions are never hard-deleted.
Assigning fields to CTI nodes
Fields are assigned from Admin → Categories. Expand a Category, Type, or Item row — each has a Fields subsection where you can assign any active field definition. Per assignment you configure:
- Visible on new — whether the field appears on the new-ticket form when that CTI node is selected.
- Required on new — whether the field must be filled before the ticket can be submitted. Only applies when visible on new is also checked.
- Sort order — drag to reorder within the node.
The fields available on a ticket are the union of all fields assigned to its selected category, type, and item, ordered by scope level (category → type → item) then sort order.
Ticket detail page
Custom field values are displayed in the Custom Fields sidebar card on the ticket detail page. Staff and admins can click Edit to update any value at any time after the ticket is created. Regular users see values in read-only form.
Tags
Tags are free-form labels that staff attach to tickets to aid categorization, search, and filtering beyond the formal CTI hierarchy. They are managed from Admin → Tags.
How tags are created
Tags are created on first use — there is no separate "create tag" step. When a staff member adds a tag to a ticket and types a name that doesn't exist yet, the tag is created automatically. Tags are stored lowercase regardless of what was typed.
Autocomplete
As staff type in the tag field on a ticket, existing active tags matching the prefix are suggested. Staff can select a suggestion or press Enter to create and apply a new tag with whatever they've typed.
Deactivating a tag
If an inappropriate or misspelled tag is created, admins can deactivate it from Admin → Tags by clicking Deactivate. Deactivated tags:
- No longer appear in autocomplete suggestions.
- Cannot be re-created by staff — if someone types the exact name of a deactivated tag, they receive an error explaining that only an admin can restore it.
- Remain visible on tickets that already carry them (historical record is preserved).
Restoring a tag
Admins can restore a deactivated tag by clicking Restore on the Tags admin page. The tag becomes active again and reappears in autocomplete.
Tag rules at a glance
| Action | Who can do it |
|---|---|
| Add a new tag (first use creates it) | Staff, Admin |
| Add an existing active tag | Staff, Admin |
| Remove a tag from a ticket | Staff, Admin |
| Deactivate a tag | Admin only |
| Restore a deactivated tag | Admin only |
| Re-create a deactivated tag name | Not allowed — restore instead |
Ticket statuses
Manage statuses from Admin → Statuses. Statuses represent where a ticket is in its lifecycle.
System statuses
These are created automatically on first run and have special behavior. Their names and colors can be changed, but they cannot be deactivated or deleted.
| Status | Special behavior |
|---|---|
| New | The initial status assigned to every newly created ticket. |
| Resolved | Setting this status starts the reopen window timer. Users can reopen the ticket by replying within this window. When set, staff are prompted to enter optional resolution notes. |
| Closed | Automatically set by a background job when the reopen window expires. No further user-initiated updates are allowed. Staff and admins can still change the status manually. |
Custom statuses
Create custom statuses to represent intermediate states meaningful to your workflow. Common additions:
- In Progress — being actively worked
- Pending — waiting on the user
- Awaiting Vendor — blocked on a third party
- On Hold — intentionally paused
- Escalated — passed to a specialist or manager
Each custom status has:
- Name — shown in the ticket detail and list views.
- Color — hex color for the status badge.
- Sort order — controls the order in status dropdowns.
Deactivating and deleting custom statuses
Custom statuses can be deactivated — they are removed from ticket-creation dropdowns but existing tickets retain the status, and the status continues to appear in filters and reports. Deactivated statuses can be reactivated at any time.
A custom status can only be deleted permanently when zero tickets are in that status. The delete button appears only when the ticket count for that status is 0. Deleting is irreversible; deactivating is always the safer option.
Status change timeline
Every status transition is automatically recorded on the ticket. The ticket detail page shows a chronological timeline that interleaves replies and status events. Status events show the old and new status (with their colors), who made the change, and the timestamp. The initial status on ticket creation is also recorded.
Reopen window
Configure how many days after Resolved a user can reopen a ticket by replying. Set this from Admin → Settings → General → Ticket lifecycle → Reopen window (days). Set to 0 to disable user-initiated reopening entirely (staff and admins can still reopen manually).
Reopen target status
When a ticket is reopened, it is moved to the Reopen target status. Configure this from Admin → Settings → General → Ticket lifecycle → Reopen target status. The picker lists all active, non-system statuses — system statuses (Resolved, Closed) are excluded because transitioning a reopened ticket back into a terminal state would immediately re-trigger the close flow. If no target status is configured, the ticket is moved to the first active custom status alphabetically.
SLA policies
SLA (Service Level Agreement) tracking is an optional feature. Enable it from Admin → Settings → Features → SLA tracking. It can also be pre-enabled at server startup by setting the SLA_ENABLED=true environment variable.
Once the SLA tracking toggle is on, a SLA Policies management blade appears directly in the Features tab. Policies define the maximum time from ticket creation until a first response and until the ticket is resolved.
Policy fields
| Field | Description |
|---|---|
| Name | Display name for the policy, e.g. "Critical — 1h response" |
| Priority | Apply this policy to tickets of this priority: critical, high, medium, or low. |
| Category | Optional. Restrict this policy to a specific category. Leave at "All categories" for a catch-all. |
| Response target | Minutes from ticket creation until a staff reply must be added. |
| Resolution target | Minutes from ticket creation until the ticket must be marked Resolved. |
Policy matching
When a ticket is created, the system selects an SLA policy by matching in this order:
- Priority + Category — most specific match
- Priority only
- Category only
- A default policy (a policy with no Priority and no Category)
- No SLA — if no match
SLA indicators in the ticket queue
The ticket list shows a visual indicator for each ticket's SLA status:
- Green — within SLA
- Amber — within 20% of the deadline
- Red — SLA breached
Staff can sort the ticket queue by SLA status to prioritize tickets at risk of breaching.
Pausing SLA timers
When a ticket is moved to the Pending status (waiting on the user), the SLA timer is paused. The timer resumes when the ticket moves out of Pending. This prevents SLA breaches for tickets that are legitimately waiting on a response from the submitter.
Email templates
Email notification templates are edited from Admin → Email → Templates. Templates use Go's text/template syntax.
Available templates
| Template | When sent | Recipients |
|---|---|---|
ticket_created | A ticket is submitted | Ticket submitter |
ticket_assigned | A ticket is assigned to a staff member | Assigned staff member |
reply_added | A public reply is added | All participants (submitter + staff who have replied) |
ticket_resolved | Ticket status changes to Resolved | Ticket submitter |
ticket_closed | Ticket auto-closes after the reopen window | Ticket submitter |
guest_ticket_created | A guest submits a ticket | Guest (by email provided at submission) |
guest_access_link | Guest requests a new access link | Guest (by provided email) |
Template variables
{{ .Ticket.ID }} -- ticket UUID
{{ .Ticket.Subject }} -- ticket subject line
{{ .Ticket.Description }} -- ticket description body
{{ .Ticket.Status }} -- current status name
{{ .Ticket.Priority }} -- priority name
{{ .Ticket.Category }} -- category name
{{ .Ticket.Type }} -- type name (may be empty)
{{ .Ticket.Item }} -- item name (may be empty)
{{ .Ticket.TrackingNumber }} -- tracking number (guest access)
{{ .Ticket.CreatedAt }} -- creation timestamp (RFC 3339)
{{ .Submitter.Name }} -- submitter's full name
{{ .Submitter.Email }} -- submitter's email address
{{ .Assignee.Name }} -- assignee's name (in assigned template)
{{ .Reply.Body }} -- reply body (in reply_added template)
{{ .Reply.Author.Name }} -- reply author's name
{{ .ResolutionNotes }} -- resolution notes (in resolved template)
{{ .TicketURL }} -- full URL to view the ticket in the app
{{ .AppName }} -- configured application name
Template example
Subject: [{{ .AppName }}] Reply on: {{ .Ticket.Subject }}
Hi {{ .Submitter.Name }},
{{ .Reply.Author.Name }} replied to your ticket:
---
{{ .Reply.Body }}
---
View the full ticket: {{ .TicketURL }}
Ticket #{{ .Ticket.TrackingNumber }}
Preview and test
Use the Preview button on any template page to see a rendered preview with sample data. Use Send test to deliver a test email to your own address.
Webhooks
Webhooks let you push ticket events to external systems in real time. Configure them from Admin → Webhooks → New webhook.
Configuration
| Field | Description | |
|---|---|---|
URL | required | The HTTPS endpoint that receives the POST requests. |
Secret | recommended | A shared secret used to sign delivery payloads with HMAC-SHA256. Verify the signature server-side to reject spoofed requests. |
Events | required | Select which events trigger this webhook. You can subscribe to all events or specific ones. |
Enabled | Toggle deliveries on/off without deleting the configuration. |
Events
| Event | Triggered when |
|---|---|
ticket.created | A new ticket is submitted (any method) |
ticket.assigned | The assignee or assigned group changes |
ticket.status_changed | The ticket status changes to anything |
ticket.reply_added | A public reply is added to a ticket |
ticket.note_added | An internal note is added (staff-only) |
ticket.resolved | Status changes to Resolved specifically |
ticket.closed | Status changes to Closed (auto or manual) |
ticket.reopened | A Resolved or Closed ticket is reopened |
Payload format
POST https://your-endpoint.example.com/webhook
Content-Type: application/json
X-OHD-Event: ticket.reply_added
X-OHD-Delivery: 01JQXYZ...
X-OHD-Signature: sha256=<hmac-hex>
{
"event": "ticket.reply_added",
"delivery_id": "01JQXYZ...",
"occurred_at": "2026-04-05T14:32:00Z",
"ticket": {
"id": "01JQABC...",
"subject": "Laptop screen flickering",
"status": "In Progress",
"priority": "High",
"category": "Hardware",
"type": "Laptop",
"item": "Broken screen"
},
"actor": {
"id": "01JQDEF...",
"name": "Alex Chen",
"role": "staff"
},
"data": {
"reply_id": "01JQGHI...",
"body": "I've ordered a replacement screen, it arrives Thursday.",
"is_internal": false
}
}
Verifying the signature
The X-OHD-Signature header is sha256=<hex> where the hex is an HMAC-SHA256 of the raw request body, keyed with your webhook secret.
# Python
import hmac, hashlib
def verify_signature(secret: str, body: bytes, header: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode(), body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, header)
# Node.js
const crypto = require('crypto');
function verifySignature(secret, body, header) {
const sig = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(header));
}
Retries and delivery history
If the target URL returns a non-2xx status or times out (5 second timeout), the delivery is retried with exponential backoff: 1s, 2s, 4s, 8s, 16s. After 5 total attempts, the delivery is marked failed.
All deliveries (successful and failed) are logged and visible under Admin → Webhooks → [webhook name] → Deliveries. Each entry shows the event type, HTTP status, response body (truncated), and timing. You can manually re-trigger a failed delivery from this page.
Installing plugins
Plugins extend Go Help Desk with custom behavior. They run as sandboxed WASM modules. Manage them from Admin → Plugins.
Installing from a file
- Click Install plugin.
- Upload the
.wasmfile. - The system validates the plugin manifest embedded in the binary. If the manifest is missing or malformed, installation is rejected with a specific error.
- After successful validation, the plugin is listed as installed but disabled. Review its declared permissions (network hosts, capabilities) before enabling.
- Toggle the plugin to Enabled.
Installing from a URL
Click Install from URL and provide a direct URL to a .wasm file. The application downloads it, validates the manifest, and stores it locally. The URL is not called again after installation — it is not a live reference.
Plugin configuration
If a plugin declares a config_schema in its manifest, a configuration form appears on the plugin's detail page. Fill in the required fields (e.g. API keys, URLs). Configuration changes take effect immediately without a restart. Fields marked "secret": true are stored encrypted and are never returned in the admin UI after saving.
Enabling and disabling
Toggle a plugin on/off from the plugin list. Disabled plugins receive no events. The plugin binary remains installed and can be re-enabled at any time. No restart is required.
Uninstalling
Click Uninstall on the plugin's detail page. The WASM binary and all stored configuration are deleted. Custom fields or UI panels added by the plugin are removed from the ticket form. Ticket data that was set by the plugin (e.g. custom field values) is preserved in the database.
Error monitoring
The plugin list shows a count of errors per plugin over the last 24 hours. Click a plugin's name to see the error log. A plugin that errors on more than 50% of event deliveries over a 10-minute window is automatically disabled and an admin notification is generated. The threshold is configurable under Admin → Settings → Plugin error threshold.
For details on building plugins, see Plugin Development.
Branding
Customize the application's visual identity from Admin → Settings → Branding. Changes apply immediately with no restart required.
| Setting | Description |
|---|---|
| Site name | Shown in the sidebar header and browser title when no logo is set. Default: "Go Help Desk". |
| Logo |
Upload a logo file from Admin → Settings → Branding → Upload logo. Accepted formats: PNG, JPG, GIF, SVG. Maximum 2 MB.
GET /api/v1/logo with a 5-minute public cache header.
|
Branding covers site name and logo only. Layout changes, custom page sections, and additional UI components are out of scope for the built-in branding feature — use the plugin UI panel system for that.