Helpdesk Reference Public

FAQ & Troubleshooting

Answers to common questions and fixes for common problems across every Helpdesk pillar.

Guide version: r1 Module version: 0.1.0 Updated: 2026-07-22 Estimated time: 15 min 1 views

FAQ & Troubleshooting

Answers to the questions we hear most, plus concrete fixes for the problems that actually happen in production — organized pillar by pillar, and grounded in how the Helpdesk module truly behaves.

Support Agent HR / KB Manager Admin Developer
The one rule behind most "it's missing" questions Access to any Helpdesk pillar requires two conditions at once: the relevant module toggle is enabled, and the user's role grants the pillar permit. If a pillar is invisible or 404s, it is almost always one of these two — check both before anything else.

Troubleshooting quick-reference

Start here. Scan for your symptom, then jump to the detailed answer in the section below.

SymptomLikely causeFix
A pillar doesn't appear in the left menuModule toggle off, or the role lacks the pillar permit (access = both)Enable the toggle in Settings → Manage Modules and grant the permit in the role
Helpdesk permission rows are missing from the Roles editormodule_helpdesk is disabledEnable module_helpdesk; the rows surface only when the master is on
A client can't see a ticket they expectTicket isn't theirs / they're not a collaborator, or the pillar toggle is offConfirm the requester/collaborator and that module_ticket is enabled
Email replies aren't creating or updating ticketsEmail piping / IMAP not authorized, ext-imap missing, or run_imap not scheduledEnable piping + authorize IMAP, install ext-imap, schedule the cron — see Email & Automation
/knowledge_base returns 404 for everyonemodule_knowledge_base is disabled (existence is hidden)Enable module_knowledge_base
A guest can't open a KB articleArticle is a draft (not active), or the module is offPublish the article to active; confirm the module toggle
Internal Wiki content is visible to the publicIt shouldn't be — Wiki (type = help) is staff-only by designPublish public content as a KB article (type = knowledge_base) instead
A handbook upload is stuck / never produces draftsCron not running, AkbAI not configured, scanned PDF (no OCR), or max attempts hitSchedule the cron, configure AkbAI, or add sections manually
An import shows "Needs review"Scanned/image PDF, empty PDF, or processing stopped after repeated failuresOpen the review page and add/adjust sections by hand
Employees can't see a handbook articleIt's still a draft, not publishedApprove & publish it (requires handbook_publish)
The URL Shortener returns "disabled" (403) on mintAn operator turned the tool off (it ships active/public by default)Re-enable the url-shortener tool in the Tools Hub; the /s/{code} resolver stays public either way
An API call returns missing_company_key / 401Missing tenant company key or invalid/absent bearer tokenSend the company key and a valid JWT — see Developer APIs

Where to look

Which pillar owns which task, and the page that documents it in depth.

General & access

The questions that apply across every pillar — toggles, permits, and how the two combine.

A sidebar entry renders only when both gates pass: the module toggle is enabled and the signed-in user's role grants the item's permit. The menu is filtered exactly this way — for example the Handbook entry (menu key handbook) is gated by module_enabled("module_helpdesk") plus the handbook permit. If either is missing, the item is silently dropped.

Checklist:

  1. Settings → Manage Modules — is the pillar's toggle on (module_ticket, module_help, module_knowledge_base, module_page) and is the master module_helpdesk on where required?
  2. The user's role — does it grant the pillar permit (ticket, help/knowledge_base, page, handbook)?

module_helpdesk is the umbrella master toggle for the whole module (it is seeded on by a core migration, because settings is a shared per-tenant table). Under it sit four pillar sub-toggles that turn each capability on or off independently:

module_ticket module_help module_knowledge_base module_page

Important nuance during the current cutover: the pillar controllers still gate access on their own legacy toggles — Tickets on module_ticket, Internal Wiki on module_help, Knowledge Base on module_knowledge_base, and Pages on module_page. The Handbook reader gates on module_helpdesk directly. So to open a specific pillar, make sure its sub-toggle is on; the master governs which permission rows appear in Roles and the module as a whole.

All Helpdesk permission definitions live in modules/Helpdesk/config/permissions.php and are surfaced into the RBAC editor only when module_helpdesk is enabled. That's why the ticket*, help*, knowledge_base*, page* and handbook* rows vanish when the master is off.

Your saved roles are safe The keys are byte-identical to the pre-module keys, and permissions are read from the serialized users.permissions regardless of where they're defined. Toggling the module off then on again does not reset existing role grants — it only hides/shows the editable rows.

Only the Knowledge Base is a public read surface. When module_knowledge_base is enabled, an anonymous visitor can browse published KB categories and articles with no permit at all — the controller runs under App\Guards\OptionalAuthGuard, so a guest is resolved as a guest instead of being bounced to sign-in. Every other pillar (Tickets, Internal Wiki, Pages admin, Handbook) requires a logged-in user with the matching permit.

Tickets

Statuses, visibility, SLA/due dates, labels, reassignment, and the auto-close caveat.

What do the ticket statuses mean?

new open client_replied on_hold closed
StatusMeaning
newJust created, not yet worked. Both new and open tickets are candidates for auto-close.
openActively being handled by an agent.
client_repliedThe requester has responded and the ticket is awaiting the agent again.
on_holdPaused (e.g. waiting on a third party). Not actively worked.
closedResolved / no further action. Sets closed_at.

Clients only see tickets scoped to them — those they raised, or where they're a listed collaborator. If a client reports a missing ticket, verify: (1) module_ticket is enabled, (2) the client is the requester or a collaborator on that ticket, and (3) the client actually has the ticket_create/access permit through their client role. A staff agent viewing another user's ticket needs the ticket_staff access scope for that owner.

SLA is derived from the ticket type. Each ticket type carries a duration (in seconds) — the built-in choices are 1 hr (3600), 2 hr (7200), 4 hr (14400) and 8 hr (28800). When a ticket is created with a type, that duration seeds its due date. An agent with the override_ticket_due_date permit can set a custom due date, and override_ticket_type lets a user change the type on an existing ticket.

Note The current version tracks a single duration-based due date per type. Dual first-response / resolution timers and business-hours calendars are on the roadmap, not in this release.

Labels (including priority) are stored on the ticket as a comma-separated labels value and matched with FIND_IN_SET — there is no separate priority column in this version. Adding a new label from the ticket form requires the add_ticket_label permit; without it, an agent can pick existing labels but not create new ones.

Open the ticket and change its assignee via the Assigned to dropdown. Reassignment is gated by the manage capability — a user needs ticket_manage to change assignment and other ticket-management fields. Collaborators (the ticket_collaborator scope) can be added so additional people follow the thread without owning it.

The close_inactive_tickets cron job (default schedule 30 2 * * *) auto-closes tickets when the auto_close_ticket_after setting (in days) is configured. It selects tickets in new or open status, writes a system comment (ticket_auto_closed_due_to_inactivity), sets status to closed with closed_at, and logs a ticket_closed notification.

Known caveat The job currently evaluates inactivity against the ticket's created date (created_date_or_before), not a true last-activity timestamp. A long-running but still-active ticket created before the threshold can therefore be auto-closed. If auto-close is closing tickets you consider active, disable auto_close_ticket_after or lengthen it until the last-activity fix lands.

If auto_close_ticket_after is empty, the job simply skips and logs that it was not set.

Email-to-ticket

The most common "email isn't working" causes — all of which live in Tickets — Email & Automation.

Inbound mail is polled by the run_imap cron job. It skips unless both enable_email_piping and imap_authorized settings are on, and it self-throttles to once every 10 minutes. Walk this list:

  1. IMAP settings: enable email piping and authorize the mailbox so imap_authorized is set.
  2. ext-imap: the PHP imap extension must be installed and loaded — the ddeboer/imap library needs it at runtime.
  3. Cron scheduled: the run_imap job (default schedule every minute) must actually be firing via the cron runtime. If it isn't scheduled, nothing polls the inbox.
  4. Throttle window: even when scheduled every minute, the job only does real work once per 10-minute window — allow time before concluding it's broken.
Tip Full setup, mailbox configuration, and the reply-parser behavior are documented on Email & Automation.

Knowledge Base & Internal Wiki

Both pillars share the help_articles / help_categories tables, discriminated by a type column: knowledge_base (public) vs help (the staff-only Internal Wiki).

help_articlesone table
type = knowledge_basepublic KB
type = helpstaff Wiki

The controller calls show_404() up front whenever module_knowledge_base is not enabled — a disabled module hides its existence rather than showing a "disabled" page. Enable module_knowledge_base and the route resolves again. (Note: a logged-in user additionally needs the knowledge_base permit to see the in-app management experience; a guest reads the published KB with no permit.)

Two reasons. Either the module is off (see above), or the article/category isn't published. Guests are limited to active content: a category whose status isn't active, or an article whose status isn't active, returns 404 for an anonymous visitor. Logged-in staff can still preview drafts; guests cannot. The same rule guards file downloads — a guest may only download files attached to an active KB article.

The Internal Wiki is staff-only by design. Articles with type = help are served by the Help controller, which requires a logged-in user with the help (or knowledge_base) permit and the module_help toggle — a guest is redirected, never served. Only articles with type = knowledge_base are exposed on the public Knowledge Base.

So if you want an article to be internet-visible, it must be a KB article (type = knowledge_base) and published to active. Creating it as a Wiki (help) article will never make it public, no matter its status.

All KB authoring (create/edit/delete of articles and categories) lives in the permission-gated Help controller and its authenticated tab-panels — not on the public Knowledge_base route. A guest hitting a management tab-panel gets a 404. The permits are knowledge_base_create / _update / _delete for articles and knowledge_base_category_create / _update / _delete for categories. See Permissions Reference.

Company Handbook

The AkbAI-assisted PDF ingestion pipeline, and why an import might stall or land in review.

UploadPDF
Extracttext
SectionAkbAI
DraftHR review
Publishemployees

Import statuses you'll see on the admin review screen:

Queued Extracting Processing Needs review Draft Published Failed

Processing happens asynchronously in the helpdesk_process_handbook_imports cron job. Common causes, in order of likelihood:

  1. Cron not running. If the job isn't scheduled/firing, uploads sit in Queued forever. Confirm the cron runtime is executing it.
  2. AkbAI not configured. AI sectioning needs AKBAI_HOST / AKBAI_KEY. Without them the pipeline still extracts and drafts articles from the cleaned text for manual HR editing — it doesn't hang, but you won't get AI-shaped sections.
  3. Scanned / image-only PDF. There's no OCR in this version. A scan can't be read, so it's flagged Needs review for manual entry.
  4. Too many failed attempts. The job bounds retries with an attempts counter and a MAX_ATTEMPTS cap; after repeated failures it stops re-picking the row (this deliberately kills the old every-5-minute reprocessing loop). Re-upload or add sections manually.
Tip Upload a born-digital PDF (exported from a document tool), not a scan. The uploader enforces PDF-only on both the client and server.

Needs review means the pipeline couldn't confidently turn the PDF into sections and is handing it to HR to finish. The usual triggers: the PDF is scanned/image-only (no readable text, no OCR), the PDF is effectively blank/empty, or processing was stopped after repeated failures. Open the review page, read the error_message shown there, and add or adjust the sections by hand.

The employee-facing reader only shows published articles. Drafts are invisible to employees until an HR user approves and publishes them. If the reader looks empty, the handbook simply hasn't been published yet. The reader is also gated by module_helpdesk + the handbook permit, so a user without that permit won't see it at all.

Editing a draft requires handbook_update, but editing an article that is already published to employees additionally requires handbook_publish. This guards live, employee-visible policy content: publishing (and re-editing what's published) is a higher-trust action than editing an unpublished draft. Upload/import needs handbook_create; deletion needs handbook_delete.

SOP — recovering a stalled handbook import

1. Confirm the cron is alive

Verify helpdesk_process_handbook_imports is scheduled and running. A dead cron is the #1 cause of a perpetual Queued.

2. Read the review reason

Open the import's review page and read the error_message — it names the exact problem (unreadable, scanned, blank, or repeated failure).

3. Re-upload a born-digital PDF

If it was a scan or corrupt, re-upload a text-based PDF. If AkbAI is unconfigured, drafts still generate from the cleaned text for manual editing.

4. Add sections by hand if needed

For scans or blank PDFs, add the sections manually on the review screen, then approve & publish (requires handbook_publish).

Pages & short links

Web Pages, public sharing, and the tenant-isolation guarantee behind shared links.

Yes. In the current source the URL Shortener ships active · public and free — it mints /s/{code} short links from the Tools Hub. An operator can disable the tool (minting then returns 403), but the /s/{code} resolver is always public. The standard Sharable Link Generator is also available and reuses the existing shareable-result generator.

Note The short-link capability (the /s/{code} resolver and the tool_shortlinks store) is owned by the Tools module, not Helpdesk. The Pages "Share" action consumes it.

A shared link is a short opaque code of the form /s/{code} (a 7-character base-56 token — an unambiguous alphabet that omits 0/O/1/l/I). The URL carries only the code — no tenant slug, no page id, nothing decryptable from the path. The tenant's company key is stored encrypted at rest in tool_shortlinks (primary DB). On resolve, the code is looked up, the tenant key is decrypted, the correct tenant DB is switched in, Open Graph tags are emitted for the unfurl, and the target is rendered or redirected. A bad, disabled, or expired code returns 404 to hide existence.

This design means a shared page always loads the right tenant's data and never leaks tenant identifiers in the URL.

Pages is the standalone Web Pages capability (staff admin at pages, with a guest-facing per-slug renderer at about/{slug}), gated by module_page + the page permit. The public Help Center front door (help-center) renders a CMS-themed landing via the separate Content (CMS) module — a soft dependency: if Content/CMS is unavailable it 404s gracefully rather than disabling all of Helpdesk. Helpdesk reuses CMS theme chrome but does not absorb the CMS. See Web Pages & Help Center.

Developer APIs

Authentication, scopes, and the flat-controller entry points.

Every Helpdesk API call runs through the tenant + JWT + scope middleware pipeline before the controller body. missing_company_key means the request didn't include the tenant company key the multi-tenancy layer needs to resolve the right database. A 401 means the bearer token is missing, invalid, or expired. Send both a valid company key and a valid JWT bearer token. Full request shape is on Developer APIs.

The integration (client) Help Center API is read-only under scope help:read, exposing GET api/v1/help/categories, api/v1/help/categories/{id}, api/v1/help/articles and api/v1/help/articles/{id}.

The End-User API is per-employee. GET v1/api/me/help-articles is org-wide read (any authenticated employee, no extra permit — the Help Center is shared reference content, not personal data). The Tickets End-User endpoints are self-only — an employee sees and writes only their own tickets: GET v1/api/me/tickets, POST v1/api/me/tickets, GET v1/api/me/tickets/{id}, and POST v1/api/me/tickets/{id}/messages.

Yes — and it's expected. Because module-owned API controllers must be flat, uniquely-named top-level classes, they are also reachable via their bare class path (/helpdesk_api/* and /helpdesk_euapi/*) in addition to the documented api/v1/help/* and v1/api/me/* URLs. This is not an auth bypass: the same tenant + JWT + scope middleware runs from the base controller regardless of which URL reached the class. It's simply an inherent side effect of the flat-controller requirement.

Still stuck?

If nothing above matches, work through this quick escalation path before filing a report.

Check both gates

Ninety percent of "missing" reports are a module toggle off or a role permit not granted. Re-verify both — see Permissions Reference.

Read the on-screen reason

Handbook review pages surface an error_message; API responses name the failure. The system usually tells you what's wrong.

Confirm the crons are running

Email ingestion (run_imap), auto-close (close_inactive_tickets), and handbook processing (helpdesk_process_handbook_imports) all depend on a live cron runtime.

Consult the pillar page

Each pillar's dedicated page (see Overview) documents its full workflow, statuses, and edge cases in depth.

Was this guide helpful?

Report a content problem