A full-featured customer support system with ticket management, departmental routing, custom fields, and an integrated Knowledge Base: all built into a single Larapen add-on.
Ticket Management
Create, track, and resolve support tickets with status workflows, priority levels, and department routing.
Guest & Auth Support
Allow both guests and authenticated users to submit tickets. Guest tickets use name and email for identification.
Knowledge Base
Organize articles into collections with search, helpful votes, reading time, and related articles.
Custom Fields
Define dynamic form fields (text, select, checkbox, file, etc.) that appear on the ticket creation form.
AI Assistant
AI-powered reply suggestions, summarization, and translation using the Laravel AI SDK.
PDF Export
Download any ticket conversation as a formatted PDF document for archival or sharing.
Email & Ticket Bridge
Customers can answer agent emails directly: IMAP polling (or an external cron webhook) ingests inbound replies back into the ticket thread.
Use Cases
Product Support Desk
You sell software or digital products and need a structured support system.
- Create departments for each product line (e.g. “Plugin Support”, “Theme Support”).
- Add custom fields to collect product version, URL, or license key on ticket creation.
- Enable guest access so customers can submit tickets without registering.
- Use the AI assistant to draft replies and speed up response times.
Internal IT HelpDesk
Your company needs an internal ticketing system for IT support requests.
- Disable guest access: only authenticated employees can submit tickets.
- Create departments: “Hardware”, “Software”, “Network”, “Access & Permissions”.
- Use priority levels (Low, Medium, High, Urgent) for SLA tracking.
- Build a Knowledge Base with FAQs and troubleshooting guides to reduce ticket volume.
Self-Service Documentation Portal
You want a public-facing helpdesk with searchable articles organized by topic.
- Create KB collections for major topics (Getting Started, API Reference, Billing, etc.).
- Use nested collections for sub-categories.
- Enable helpful votes so users can rate articles.
- Link the “Submit a Ticket” form from article pages for issues not covered by documentation.
Requirements
- Larapen CMS v1.0.0 or later
- PHP 8.3+
- MySQL 8.0+
barryvdh/laravel-dompdf(for PDF export)laravel/ai(for AI assistant features; optional but recommended)
.env file (e.g. ANTHROPIC_API_KEY or OPENAI_API_KEY).
The rest of the add-on works without AI configuration.
Installation
Step 1: Upload the Add-on
In the admin panel, go to Admin → Extensions → Add-ons and click the Upload Add-on button. Select the add-on’s ZIP file: the system extracts it automatically and the add-on appears in the installed add-ons list.
Step 2: Activate the Add-on
Find HelpDesk & Knowledge Base in the list and click Activate. Its migrations, seeders (if any) and permissions are set up automatically.
Step 3: Configure
Navigate to Admin → HelpDesk → Settings to configure ticket workflow, guest access, custom fields, and notifications. See Configuration.
Step 4: Create Departments
Navigate to Admin → HelpDesk → Departments and create at least one department (e.g. “General Support”). Departments are required: a ticket cannot be submitted without one.
Purchase Code (License Key)
HelpDesk is sold as a separate product, so it has its own purchase code (license key), distinct from the purchase code of the main application and from the one of every other add-on. You are asked for it when you activate HelpDesk in Admin panel → Add-ons.
Our products are sold on three platforms. The way you receive a purchase code depends on where you bought the product.
| Platform / Marketplace | How you get the purchase code | Where to find it again |
|---|---|---|
| bedigit.com Store In-site purchase (Shop) |
Generated automatically when the order is paid, then sent by email, either in its own license email, or inside the order confirmation email. | My Account → My Licenses on bedigit.com |
| Gumroad | Created as soon as Gumroad notifies us of the sale, then sent in a separate email, in addition to the Gumroad receipt. | The license email, your Gumroad Library, and My Account → My Licenses on bedigit.com |
| Envato Market CodeCanyon |
Issued by Envato, not by us, and never sent by email: you download it yourself from your Envato account. | Envato account → Downloads → License certificate & purchase code |
1. bedigit.com Store (in-site purchase)
- As soon as the order’s payment status becomes Paid, a license key is generated automatically for every licensed item in the order (one key per purchased unit: buying 3 units gives 3 distinct keys).
- It is emailed to the address used on the order, either in a dedicated license email or inside the order confirmation email. Check your inbox and your spam / junk folder.
- The key stays available in your account under My Account → My Licenses. Keys are masked in the list; open the license detail page to reveal and copy the full key, see the domains it is activated on, and deactivate a domain to free an activation slot.
- The matching invoice is under My Account → My Orders.
2. Gumroad
- A Gumroad purchase produces two separate emails: the Gumroad receipt (sent by Gumroad, giving access to the files) and a license key email (sent by bedigit.com) that contains your purchase code.
- The license key email is generated as soon as Gumroad notifies us of the sale, so it normally arrives within seconds of the payment. Here too, check your inbox and your spam / junk folder.
- When the Gumroad product uses Gumroad’s own license-key feature, the same key also appears in your Gumroad receipt and under Library → your purchase on gumroad.com.
- Use the same email address on bedigit.com as on Gumroad: your keys are then linked to your account automatically and listed under My Account → My Licenses, even if you register after the purchase. You can also add a Gumroad key manually from My Account → My Gumroad Licenses.
3. Envato Market (CodeCanyon)
- Envato purchase codes are issued and delivered by Envato Market, never emailed by us, so there is nothing to look for in your spam folder: you retrieve the code from your Envato account.
- Log in to your Envato / CodeCanyon account, open the Downloads page, find the item, and choose License certificate & purchase code from the Download dropdown. The code is written in that certificate.
- An Envato purchase code looks like
12345678-90ab-cdef-1234-567890abcdef(8-4-4-4-12 characters). It never changes, and renewing item support does not issue a new one. - Official Envato article: Where Is My Purchase Code?
Configuration
Settings are managed in Admin → HelpDesk → Settings
(stored in the settings table, group helpdesk).
Config file defaults are in config/helpdesk.php.
Ticket Settings
| Setting | Description | Default |
|---|---|---|
helpdesk_guest_access |
Allow non-authenticated users to create tickets. | true |
helpdesk_items_per_page |
Number of tickets per page in lists. | 15 |
helpdesk_auto_close_resolved_days |
Auto-close resolved tickets after N days of inactivity. Set to 0 to disable. | 0 |
helpdesk_allow_customer_close |
Allow customers to close their own tickets from the front-end portal. | true |
helpdesk_allow_priority_selection |
Show priority selector on the ticket creation form. | false |
helpdesk_allow_attachments |
Allow file attachments on tickets and replies. | true |
helpdesk_max_attachment_size |
Maximum attachment file size in KB. | 5120 (5 MB) |
helpdesk_replies_order |
Display order of replies: oldest_first or newest_first.
Inbound email replies use the email’s Date: header as created_at,
so this ordering applies to ingested mail as well. |
oldest_first |
helpdesk_tickets_order |
Order of the admin ticket lists: priority_date (priority first, then date)
or date (date only). |
date |
helpdesk_response_time |
Expected response time displayed to customers. | 48 |
helpdesk_response_time_unit |
Unit for response time: minutes, hours, days, weeks. |
hours |
helpdesk_reply_draft_autosave_enabled |
Auto-save the reply being written on the admin ticket page (text, internal-note switch and attached files), per ticket and per agent. | true |
helpdesk_reply_draft_autosave_interval |
Seconds between two auto-saves while the reply form has unsaved changes (5–600). | 30 |
helpdesk_reply_draft_manual_save_enabled |
Show a Save as draft button on the reply form. When off, the button is hidden. Both draft switches off disables drafts altogether. | true |
helpdesk_reply_form_position |
Where the reply form sits on the admin ticket page: before or after the ticket messages. |
after |
helpdesk_reply_editor_height |
Height in pixels of the admin reply editor when it opens (60–1000). It grows with the reply being written. Other editors keep the global WYSIWYG height. | 150 |
helpdesk_reply_editor_max_height |
Height in pixels the admin reply editor stops growing at; a longer reply scrolls inside it. 0 removes the cap. |
500 |
Notification Settings
HelpDesk notifications are registered as notification types, so they are switched on and off
under Admin → Settings → Notifications, not on the HelpDesk
settings page. Each type stores one setting, and the master switch
notifications_enabled turns every notification off at once. Users can additionally
opt out per type and per channel from their own notification preferences.
| Setting | Description | Default |
|---|---|---|
notification_helpdesk_new_ticket_admin |
Notify all admin users when a new ticket is created. | true |
notification_helpdesk_ticket_confirmation |
Send the confirmation email to the ticket author after submission. | true |
notification_helpdesk_new_reply |
Notify the ticket author when an agent posts a reply (internal notes are excluded). | true |
helpdesk_mail_from_address |
Sender address for HelpDesk emails (Admin → HelpDesk → Settings → Notifications). Empty falls back to the site’s default sender. | empty |
helpdesk_mail_from_name |
Sender name for HelpDesk emails. Empty falls back to the site’s default sender name. | empty |
Knowledge Base Settings
| Setting | Description | Default |
|---|---|---|
kb_require_auth |
Require authentication to access the Knowledge Base. Articles whose visibility is always_public stay readable by everyone. |
false |
kb_items_per_page |
Number of articles per page. | 12 |
kb_show_search |
Display the search bar on the KB index page. | true |
kb_show_reading_time |
Show estimated reading time on articles (calculated at ~200 words/min). | true |
kb_show_helpful_votes |
Display “Was this helpful?” yes/no voting on articles. | true |
kb_show_related_articles |
Show related articles from the same collection at the bottom of article pages. | true |
kb_show_descendant_articles |
On a collection page, also list the articles of all its sub-collections (at any depth), not only its own articles. | false |
Allowed Attachment Types
The following file types are accepted for attachments (configured in config/helpdesk.php):
Admin: Dashboard
The Dashboard (HelpDesk → Dashboard) provides a combined view of all tickets with statistics.
Stats Cards
Aggregate counts displayed at the top:
- Total: all tickets in the system
- Open: tickets with
openstatus - In Progress: tickets being actively worked on
- Waiting: combined count of
waiting_customerandwaiting_agent - Resolved: tickets marked as resolved
- Closed: permanently closed tickets
Charts
Below the stats cards, four charts describe the tickets opened over a period chosen with the buttons at the top of the page (Last 7 days, 30 days, 90 days or 12 months). The stats cards are the live backlog and do not depend on the period.
- Ticket Volume: tickets opened and tickets closed per day (per week over 90 days, per month over 12 months)
- Tickets by Status: the current status of the tickets opened over the period
- Tickets by Department: where those tickets were filed, busiest department first (tickets without a department count as Unassigned)
- Top Customers: the registered customers who opened the most tickets
Every bar and slice is a link: clicking it opens the All Tickets list already filtered by that status, department or customer.
Admin: Tickets
Filtered Ticket Lists
The Tickets sidebar entry opens the Open list and carries a badge with the number of open tickets. From there, the dropdown button at the top of the page switches between the pre-filtered views, each showing only tickets in a specific status group:
| List | Statuses Included |
|---|---|
| Open | open |
| On Hold | on_hold |
| In Progress | in_progress, waiting_customer, waiting_agent, no_response |
| Resolved | resolved |
| Closed | closed |
| All Tickets | every status |
| No Response | no_response |
Each list supports additional filters for priority, department, customer, and search.
The lists are ordered by date by default, most recent first: by the customer’s last reply in the Open list, and by the last message activity (a reply added or edited) in the other lists. Set HelpDesk → Settings → Display & Behavior → Tickets Order to By priority, then by date to list urgent tickets first, then high, medium and low ones, each level keeping the date order. To answer some customers first, give their tickets a higher priority with a ticket filter (the Set the priority action).
In every list, a row opens the ticket, and three of its cells are also real links, so they can be opened in a new tab from the right-click menu:
- the Subject opens the ticket;
- the Submitted By name opens the All Tickets list filtered by that customer (for a guest, the list is searched by their email);
- the Department opens the All Tickets list filtered by that department.
Ticket Detail Page
The ticket detail page (HelpDesk → Tickets → {reference}) shows:
- Ticket header: reference number, subject, status badge, priority badge, department, submitter info, creation date
- Status/Priority/Department controls: inline dropdown selectors to change status, priority, or department (AJAX-powered, returns JSON responses)
- Conversation thread: all replies in chronological order (configurable via
helpdesk_replies_order). Each reply shows author name, admin/customer badge, timestamp, body text, and attachments - Internal notes: admin-only notes visible only to staff, styled differently from customer replies
- Custom field values: the values submitted for all custom fields on the ticket
- Recent tickets: sidebar panel showing up to 10 recent tickets from the same customer
- Envato purchases: if the Envato add-on is active and the ticket has a linked user, shows the user’s verified Envato purchases
Replying to Tickets
The reply form at the bottom of the ticket detail page supports:
- Reply body: rich text area for the response
- Internal note toggle: marks the reply as an internal-only note (not sent to customer, not visible on front-end)
- File attachments: attach files to the reply
- Drafts: the unsent reply (text, internal-note switch and attached files) is kept per ticket and per agent. It is auto-saved on a timer and/or saved with the Save as draft button, depending on the Reply Drafts settings; Discard draft drops it. Files saved with the draft move to the reply when it is sent and count against the attachment limit
- Reply action: after sending, choose to: stay on the ticket, go to the ticket list, or jump to the next ticket
- Edit/Delete replies: admin users can edit the body of any reply or delete replies entirely (AJAX-powered)
Auto-Status Updates on Reply
When a reply is posted on an open ticket:
- Admin reply → status automatically changes to
in_progress - Customer reply → status automatically changes to
open - On-hold tickets stay
on_holdon a customer reply or an internal note; a public admin reply moves them toin_progress - Closed/resolved tickets are not auto-updated
On Hold Tickets
Set a ticket to On Hold (status selector on the ticket page) when it does not need an urgent reply and you want to answer it properly later. The ticket leaves the Open list and the sidebar Tickets badge, and waits in the On Hold list of the switcher.
- The customer never sees On Hold: on their side the ticket is still shown as Open.
- A new customer message keeps the ticket on hold, and so does an internal note.
- A public agent reply takes it out of the hold and moves it to
in_progress. - The automatic No Response flagging skips on-hold tickets.
Merge & PDF Export
Merging Tickets
Admin can merge a source ticket into a target ticket via POST admin/helpdesk/tickets/{ticket}/merge.
This operation:
- Moves all replies from the source to the target ticket
- Moves ticket-level attachments (morph relation) to the target
- Deletes custom field values from the source (not transferable)
- Deletes the source ticket record
The entire operation runs inside a database transaction.
PDF Export
Click the Download PDF button on any ticket detail page to generate a formatted PDF containing
the ticket header, all replies, and metadata. Uses barryvdh/laravel-dompdf.
Admin: Ticket Filters
Ticket filters are rules that act on tickets automatically: close them, move them to the trash, route them to a department, change their priority, and more. Managed via HelpDesk → Ticket Filters.
How Filters Run
- On new tickets: every active filter with Run on new tickets enabled is checked as soon as a ticket is created, from the top of the list down. Each matching filter applies its actions. Use Reorder Filters to change the order.
- Stop here: when a filter with this option matches, the filters below it are skipped for that ticket.
- On existing tickets: click the play button of a filter to see how many existing tickets match (with a sample list), then apply the filter to all of them. Tickets in the trash are never included.
Filters run before any notification is sent, so a filter that trashes a spam ticket sends no notification to the customer or the agents (only the alert and forward e-mails the filter itself names still go out).
Conditions
Choose whether all conditions or any condition must match. Available conditions:
| Group | Condition | Comparisons |
|---|---|---|
| Customer | Customer (registered account), Customer e-mail (account or guest e-mail), Customer type (registered or guest) | Is one of / is not one of; text comparisons for the e-mail (contains, equals, starts with, ends with...) |
| Product | Owned product: the customer holds an active, unexpired license key for one of the chosen products (requires the Licenses add-on) | Is one of / is not one of |
| Ticket | Department, Assigned agent (including “Unassigned”), Status, Priority, Channel (web form, e-mail, API), Subject, Customer message, IP address | Is one of / is not one of; text comparisons |
| Ticket | Ticket age, Inactivity (days since the last message) | More than / less than a number of days |
Text comparisons ignore case. Tip: Customer e-mail ends with @example.com targets a whole company.
Actions
- Set status: choose Closed to close tickets automatically (recorded as closed by the system).
- Set priority and Assign to department.
- Assign to agent: the agent is notified by e-mail. Agents are administrators and every user allowed to view the helpdesk tickets. Tickets can also be assigned by hand from the agent picker in the ticket header, and the ticket lists have an Agent column and filter, with a My tickets toggle.
- Reply with a canned response: posts one of your canned responses and e-mails it to the customer.
- Add an internal note: a note only agents can see.
- Send an e-mail alert: a short alert (reference, subject, customer and a link to the ticket) to up to 10 addresses, separated by commas.
- Send the ticket by e-mail: the whole ticket (details, every message except internal notes, and the attachments up to 10 MB) to up to 10 addresses. Replying to that e-mail writes to the customer.
- Mute agent alert: on new tickets only, agents are not notified.
- Move to trash: the ticket can still be restored from the trash.
Actions always run in the same order: routing and priority first, then messages, then the status, and the trash last. The Preview button of the filter form counts the matching tickets before you even save.
Admin: Departments
Departments organize tickets into logical groups (e.g. “Sales”, “Technical Support”, “Billing”). Managed via HelpDesk → Departments.
Department Fields
| Field | Description |
|---|---|
| Name (translatable) | Display name shown to customers in the department selector. |
| Slug (translatable) | URL-friendly identifier. |
| Description (translatable) | Optional description for admin reference. |
| Optional contact email for the department. | |
| Is Active | Only active departments appear in the ticket creation form. |
| Position | Sort order in dropdowns and lists. |
The department list page shows ticket counts per department. Standard CRUD operations: create, edit, delete.
Creating a Department from a License Product
When the Licenses add-on is active, you can spin up a support department for a licensed product straight from the licenses catalog. On Licenses → Products each product carries a Create support department button (a headset icon on the row, and a button in the product's Edit modal). One click:
- Creates a helpdesk department named after the product (translations, slug, and active state carried over), or updates the existing one if you have already created it: it never makes duplicates.
- Automatically adds a license → department access link, so the department is gated behind ownership of that product (see Department Gating). Existing links on the department are preserved.
Admin: Custom Fields
Custom fields extend the ticket creation form with additional data collection. Managed via HelpDesk → Custom Fields.
Supported Field Types
| Type | Description | Has Options? |
|---|---|---|
text |
Single-line text input (max 255 characters) | No |
textarea |
Multi-line text input (max 5000 characters) | No |
select |
Dropdown select with predefined options | Yes |
checkbox |
Multiple checkboxes (values stored as JSON) | Yes |
radio |
Radio buttons with predefined options | Yes |
number |
Numeric input | No |
email |
Email address input with format validation | No |
date |
Date picker input | No |
file |
File upload (stored via MediaService) | No |
Custom Field Properties
| Field | Description |
|---|---|
| Label (translatable) | Display label shown to the user. |
| Name | Internal identifier (used as form field name). |
| Type | One of the 9 supported types above. |
| Options | Array of valid values (only for select, checkbox, radio types). |
| Placeholder (translatable) | Placeholder text for the input. |
| Is Required | Whether the field is mandatory on ticket creation. |
| Is Active | Only active fields are shown on the form. |
| Position | Sort order on the form. |
Dynamic Validation
The CustomFieldService::buildValidationRules() method automatically generates Laravel validation rules
from the active custom field definitions. Rules are type-aware (e.g. email type adds email validation,
select/radio validate against the defined options, file validates size limits).
Admin: Knowledge Base
KB Collections
Collections group articles into categories. Managed via HelpDesk → KB Collections.
- Nestable: collections support a parent/child hierarchy, and their front-end URL follows it: a collection “Guides” under “Product” lives at
/support/product/guides, and its articles at/support/product/guides/article-slug. - Slugs: a collection slug only has to be unique among the collections that share its parent, so two collections under different parents can have the same name and slug. At the top level,
tickets,search,articleandvoteare reserved for other support pages. - Translatable: name, slug, and description support multiple languages.
- Icon: optional Bootstrap Icon class (e.g.
bi-book) displayed on the front-end. - Active/Inactive: only active collections are shown on the front-end.
- Position: controls sort order.
KB Articles
Articles are rich-content documents within collections. Managed via HelpDesk → KB Articles.
Article Fields
| Field | Description |
|---|---|
| Title (translatable) | Article title displayed in lists and as the page heading. |
| Slug (translatable) | URL-friendly identifier, translatable for each language. It must be unique across the whole knowledge base; left empty, it is generated from the title (with a numeric suffix when already taken). |
| Content (translatable) | Full article body (HTML content). |
| Excerpt (translatable) | Short summary shown in article listings. |
| Meta Title / Meta Description (translatable) | SEO metadata overrides. |
| Collection | Which KB collection this article belongs to. |
| Status | draft, published, or archived. |
| Visibility | public (visible to everyone), always_public (visible to everyone, even when the Knowledge Base requires a login) or auth_only (requires login). |
| Position | Sort order within the collection. |
Computed Properties
- Reading time: calculated from word count at ~200 words per minute.
- Helpful percentage: ratio of yes votes to total votes (null if no votes).
- View count: incremented each time the article is viewed on the front-end.
Article List
The admin article list is paginated and filterable by status, collection, and search term. Columns: title, collection, status badge, visibility, view count, position.
KB Settings
A separate settings page at HelpDesk → KB Settings controls Knowledge Base display options (see Configuration: Knowledge Base Settings).
Admin: Settings
The settings page (HelpDesk → Settings) is organized into sections:
Ticket Configuration
- Guest access toggle
- Items per page
- Auto-close days
- Allow customer close toggle
- Allow priority selection toggle
- Allow attachments toggle + max size
- Replies order (oldest first / newest first)
- Reply drafts: auto-save toggle and interval, manual save toggle
- Reply form: position (before or after the messages), editor height and maximum height (the editor grows with its content)
- CAPTCHA toggle (integrates with core CaptchaService)
- Response time and unit
Notification Configuration
- Notify admins on new ticket
- Send confirmation to ticket author
- Notify author on new reply
Knowledge Base Configuration
- Guest access / require auth
- Items per page
- Show search / reading time / helpful votes / related articles
Email Bridge Configuration
- Master toggle + outbound reply embedding
- Correlation strategy (headers / plus-addressing / subject tag)
- Unknown-sender autoresponder + per-sender rate limit
- Closed-ticket autoresponder (a customer's emailed reply never lands on a closed ticket)
- IMAP credentials, folder and polling interval
- Connect retries / backoff for flaky providers
- Webhook token (regenerable from this page)
- See Email Bridge for the full reference.
Admin: Email & Ticket Bridge
The Email Bridge lets customers reply to an agent’s email notification and have that reply appear as a new message on the ticket. It is a two-way bridge:
- Outbound: when the bridge is enabled, the reply body is embedded directly in
NewReplyNotificationemails so the customer can answer inline. - Inbound: a poller reads the configured IMAP mailbox, correlates each message with an existing ticket, and appends it as a new reply.
- Autoresponder: mail from addresses that cannot be correlated with a ticket triggers a polite reply that points the sender to the public ticket creation URL.
- Closed tickets: a closed ticket only accepts agent replies. A customer email matched to one is not appended; the sender gets a reply explaining the ticket is closed, with a link to open a new one.
All settings are editable under Admin → HelpDesk → Settings → Email Bridge
and are persisted in the settings table (group helpdesk). The config file
defaults live in config/helpdesk.php under the email_bridge and imap
keys.
Bridge Settings
| Setting | Description | Default |
|---|---|---|
helpdesk_email_bridge_enabled |
Master toggle. When false, the CLI command, scheduler and webhook all early-return. |
false |
helpdesk_email_embed_reply_body |
Embed the agent reply into outbound notification emails so customers can answer inline. | true |
helpdesk_email_unknown_sender_autoresponder |
Send a polite autoresponder to senders whose mail cannot be correlated to any ticket. Suppressed in backfill mode to avoid replying to old mail. | true |
helpdesk_email_closed_ticket_autoresponder |
Answer a customer who emails a reply to a closed ticket, telling them the ticket is closed and linking the ticket creation page. The reply is refused either way; turning this off only silences the courtesy mail. | true |
helpdesk_email_correlation_strategy |
How inbound mail is matched to a ticket: header (In-Reply-To / References),
plus_addressing (support+REF@domain) or subject_tag
([#REF]). |
header |
helpdesk_email_plus_addressing_prefix |
Local part used when plus_addressing is active (e.g. support). |
support |
helpdesk_email_subject_tag_format |
Tag format for subject_tag correlation. Must contain the :ref placeholder. |
[#:ref] |
helpdesk_email_rate_limit_per_hour |
Max inbound replies accepted per sender per rolling hour. Bypassed in backfill mode. | 10 |
IMAP Mailbox Settings
| Setting | Description | Default |
|---|---|---|
helpdesk_imap_host / helpdesk_imap_port |
Server host and port (usually 993 for IMAPS, 995 for POP3S). |
: / 993 |
helpdesk_imap_encryption |
One of ssl, tls, starttls, none. |
ssl |
helpdesk_imap_validate_cert |
Reject self-signed or otherwise invalid server certificates. | true |
helpdesk_imap_protocol |
Mail protocol: imap or pop3. |
imap |
helpdesk_imap_username / helpdesk_imap_password |
Mailbox credentials. The password is stored encrypted in the database. | : |
helpdesk_imap_folder |
Mailbox folder to poll. | INBOX |
helpdesk_imap_poll_interval |
Scheduler interval in minutes: one of 1, 5, 10,
15, 30, 60. |
5 |
helpdesk_imap_max_per_poll |
Maximum messages fetched per poll (upper-bounded to 500 by the controller/command). | 50 |
helpdesk_imap_connect_retries |
Extra connect attempts after the first transient failure
(e.g. OVH “Connection refused” when a prior session is still held). 0 disables retry. |
1 |
helpdesk_imap_connect_backoff_seconds |
Delay between connect retries. | 3 |
Scheduler & CLI Command
Once the bridge is enabled, the add-on registers a scheduled Artisan command automatically:
php artisan helpdesk:fetch-inbound
The service provider builds the cron expression from helpdesk_imap_poll_interval
(e.g. */5 * * * * for 5 minutes, 0 * * * * for 60). The job runs with
withoutOverlapping(10), runInBackground() and onOneServer().
The command also accepts two options for manual runs:
| Option | Description |
|---|---|
--limit=N |
Maximum number of messages to fetch in one poll. Defaults to 50; capped at 500. |
--backfill |
Include already-read (\Seen) messages. Suppresses the autoresponder and bypasses the
per-sender rate limit so long historical threads can be imported safely. |
helpdesk_replies.inbound_raw_hash). Re-running the command or the webhook will not
create duplicates.
Inbound Webhook (External Cron Trigger)
Some hosts block outbound IMAP from the CLI path but allow it from PHP-FPM. In that case an external scheduler (cron-job.org, UptimeRobot, GitHub Actions, etc.) can drive the bridge by hitting the webhook URL:
/helpdesk/inbound/fetch/{token}
Route name: helpdesk.inbound.fetch. Throttled to 30 requests per minute.
Path Parameter
token |
Required | The shared token from helpdesk_fetch_inbound_token (32+ alphanumeric characters).
Compared with hash_equals(); any mismatch returns 404.
Rotate it from Settings → Email Bridge if it leaks. |
Query Parameters
backfill |
Optional | When truthy (1, true, yes), fetches already-read
messages and suppresses both autoresponder and rate limit. Same semantics as the CLI option. |
limit |
Optional | Overrides helpdesk_imap_max_per_poll for this request. Clamped to [1, 500]. |
Responses
200 bridge_disabledwhenhelpdesk_email_bridge_enabledis off.200with a one-line ingest report on success:Fetched N · replies=N · autoresponders=N · dup=N · auto-skip=N · own-loop=N · rate-limit=N · empty=N · closed=N · err=N404on missing or invalid token.500 fetch_failed: <reason>on connection or IMAP errors.
Backfilling Existing Mailboxes
Enable the bridge on a mailbox that already has history, then import the existing replies with one run:
php artisan helpdesk:fetch-inbound --backfill --limit=500
or via the webhook:
GET /helpdesk/inbound/fetch/{token}?backfill=1&limit=500
Backfill mode:
- Includes already-read (
\Seen) messages (normal polls useunseen()only). - Suppresses the autoresponder so old, unrelated mail is not answered.
- Bypasses the per-sender rate limit for long historical threads.
- Preserves each reply’s original
Date:header as itscreated_at, so the ticket thread respects the Newest First / Oldest First ordering configured in settings. - Is idempotent: the SHA-256 hash of the raw message body (
inbound_raw_hash) blocks duplicates.
Admin: AI Assistant
The AI assistant is available on the ticket detail page and provides context-aware AI actions powered by the Laravel AI SDK.
Available Actions
| Action | Description |
|---|---|
suggest_reply |
Generate a professional reply to a single customer message. |
suggest_reply_conversation |
Generate a reply based on the full ticket conversation history. The current message
is marked with [CURRENT MESSAGE] for the AI to focus on. |
suggest_reply_articles |
Generate a reply referencing relevant Knowledge Base articles. Up to 5 articles are searched by keyword match and view count. |
summarize |
Summarize the key points of a customer message. |
translate_english |
Translate the message into English. |
translate_french |
Translate the message into French. |
make_shorter |
Condense a message while preserving its meaning. |
custom_prompt |
Process the text with a custom instruction provided by the admin. |
Agent Configuration
The TicketAiAssistantAgent is configured with:
Temperature(0.7): balanced creativity and accuracyMaxTokens(4096): allows for detailed responses- The AI model is read from
setting('ai_default_model')or falls back to the default provider model
admin/helpdesk/ai-assistant/generate
Request Body
action |
Required | One of the actions listed above |
text |
Required | The message text to process |
ticket_id |
Optional | Required for suggest_reply_conversation |
options |
Optional | Array with: avoidMarkdown, avoidEmDash, customPrompt |
Response (JSON)
Front-end: Ticket Portal
Routes
| Method | URL | Route Name | Auth? | Description |
|---|---|---|---|---|
| GET | /{locale}/support/tickets/new |
helpdesk.create.localized |
Guest* | Ticket creation form |
| POST | /{locale}/support/tickets |
helpdesk.store.localized |
Guest* | Submit a new ticket |
| GET | /{locale}/support/tickets/confirmation/{reference} |
helpdesk.confirmation.localized |
Guest* | Ticket confirmation page |
| GET | /{locale}/support/tickets |
helpdesk.tickets.localized |
Yes | My tickets list |
| GET | /{locale}/support/tickets/{reference} |
helpdesk.show.localized |
Yes | View ticket detail & conversation |
| POST | /{locale}/support/tickets/{reference}/reply |
helpdesk.reply.localized |
Yes | Post a customer reply |
| POST | /{locale}/support/tickets/{reference}/close |
helpdesk.close.localized |
Yes | Close a ticket |
* Guest access depends on the helpdesk_guest_access setting.
Non-localized variants (without {locale}) are also registered.
Ticket Creation Form
The creation form includes:
- Subject: required text field (max 255 characters)
- Department: required dropdown of active departments
- Message: required text area (max 10,000 characters)
- Priority: optional dropdown (only shown if
helpdesk_allow_priority_selectionis enabled) - Guest fields: name and email fields (only shown for non-authenticated users)
- Custom fields: all active custom fields are rendered dynamically
- Attachments: file upload (up to 5 files, if attachments are enabled)
- CAPTCHA: shown if enabled via
helpdesk_captcha_enabled
Ticket Reference Numbers
Each ticket is assigned a unique reference number in the format HD-7KQ4M9XP2R, auto-generated by the
TicketObserver during creation. References are random (10 characters, without the look-alike 0/O and 1/I/L), so a reference cannot be guessed from another one.
Customer Ticket View
The front-end ticket detail page shows:
- Ticket header (reference, subject, status, priority, department)
- Conversation thread: internal notes are filtered out (not visible to customers)
- Reply form for posting additional messages
- Close button (if
helpdesk_allow_customer_closeis enabled and ticket is not already closed) - Custom field values
Access Control
- Authenticated users can only see their own tickets (
user_idmatch) - Admin users can see all tickets
- Closed tickets cannot receive customer replies: from the ticket page or by email. Agents and admins can still post a reply, which does not reopen the ticket
Front-end: Knowledge Base
Routes
| Method | URL | Route Name | Description |
|---|---|---|---|
| GET | /{locale}/support |
kb.index.localized |
KB home: collections, popular & recent articles, search |
| GET | /{locale}/support/{collection-path} |
kb.path.localized |
Collection page, at its nested path (e.g. /support/product/guides): articles in the collection + child collections |
| GET | /{locale}/support/{collection-path}/{article} |
kb.path.localized |
Article page under the collection it is viewed from: full content + related articles + voting. The canonical URL is always under the article’s primary collection |
| GET | /{locale}/support/article/{slug} |
kb.show.localized |
Legacy article URL: 301 to the article’s nested URL |
| POST | /{locale}/support/vote/{id} |
kb.vote.localized |
Vote article as helpful/not helpful (AJAX) |
Non-localized variants (without {locale}) are also registered.
KB Index Page
- Search bar: full-text search across article titles, content, and excerpts (if
kb_show_searchis enabled) - Collections grid: root-level collections with article counts
- Popular articles: top 5 articles by view count
- Recent articles: 5 most recently published articles
Collection Page
- Collection name, description, and icon
- Paginated list of published articles in the collection
- Child collections (if any)
Article Page
- Full article content with reading time estimate
- Helpful votes (“Was this helpful?” yes/no buttons, AJAX-powered)
- Related articles from the same collection
- SEO metadata (meta title / meta description from article fields or auto-generated)
Visibility & Access
- Public articles are visible to everyone
- Always Public articles are visible to everyone, even when the KB requires authentication: use it for a guide that visitors need before they have an account. A visitor who is not logged in only sees the other Always Public articles in the sidebar and related articles of such a page
- Auth Only articles are visible only to authenticated users
- If
kb_require_authis enabled, the entire KB requires authentication, except the Always Public articles
Licenses & Envato Add-on Integration
Purchase-based access control is driven by the Licenses add-on, which covers license keys from every provider (Envato, Gumroad, the Shop, manual). The Envato Market Integration add-on adds the buyer's verified Envato purchases to the agent's view of a ticket. Both settings below live under Admin → HelpDesk → Settings → Licenses Integration.
Department Gating
When the Licenses add-on is active and licenses_helpdesk_require_purchase is enabled:
- The ticket creation form filters the department list to only show departments the user holds an active license for.
- Access is determined by
LicensesAccessService::getAccessibleEntityIds()using theHelpdeskDepartmentlinkable type, and the submitteddepartment_idis re-checked server-side so a hand-crafted request cannot bypass the dropdown. - Departments with no linked product remain unrestricted.
KB Collection Gating
When licenses_kb_restrict_by_purchase is enabled:
- Article pages check whether the article’s collection has linked products
(
HelpdeskKbCollectionlinkable type). - Visitors without a matching license are sent to their licenses page, where they can register a missing key.
- Unauthenticated users are redirected to the login page.
Ticket Detail Context
The admin ticket detail page shows the customer’s verified Envato purchases in a sidebar panel (if the user has a linked account), giving agents immediate context about what products the customer owns.
Notifications
The add-on sends four types of email notifications:
| Notification | Recipient | Trigger | Setting |
|---|---|---|---|
NewTicketAdminNotification |
All admin users | New ticket created | notification_helpdesk_new_ticket_admin |
TicketConfirmationNotification |
Ticket author | New ticket created | notification_helpdesk_ticket_confirmation |
NewReplyNotification |
Ticket author | Admin posts a reply (not internal notes) | notification_helpdesk_new_reply |
Notification::route('mail', $email) (on-demand notification routing).
Updating
There are two ways to update this add-on: via the admin panel (recommended) or manually replacing files.
Method 1: Admin Panel Upload (Recommended)
- Download the latest
.zipfile of this add-on. - Go to Admin panel → Add-ons and click the Upload button.
- Select or drag the
.zipfile into the upload area. - A confirmation prompt will show the current and new version numbers. Click Replace to proceed.
- Go to Admin panel → System Update (
/admin/update) to apply any pending database migrations.
Method 2: Manual File Replacement
Step 1: Replace Files
Replace the add-on directory with the new version.
Step 2: Run Migrations
php artisan migrate
Pending migrations run once, so the command is safe to repeat.
Step 3: Clear Caches
php artisan config:clear
php artisan route:clear
php artisan view:clear
Step 4: Verify
Visit HelpDesk → Dashboard and confirm the stats cards and the charts load correctly. Check the Knowledge Base front-end page to verify articles are displayed.
Uninstallation
Switching an add-on off without losing anything is a deactivation: go to Admin panel → Add-ons, find HelpDesk & Knowledge Base and click Deactivate.
- Its routes, views, admin menu entries and permissions stop being registered, and its front-end pages stop answering.
- Its database tables and all the data they hold are kept, and its files stay under
extensions/addons/helpdesk/. Nothing is deleted. - The purchase code recorded at activation is kept too, so activating the add-on again does not ask for it.
- Deactivation is refused while another active add-on depends on this one: deactivate that add-on first.
Click Activate on the same card to switch it back on. Pending migrations are re-run, assets are republished, and the add-on picks up exactly where it left off.
Removing
Removing is permanent and destroys the add-on's data. The Remove button only appears on a deactivated add-on, so removal is always two steps:
- Deactivate HelpDesk & Knowledge Base (see Uninstallation).
- Click Remove on its card and confirm the prompt.
The admin panel then, in one pass:
- runs the add-on's uninstall hook, if it ships one, while its code is still on disk;
- revokes the permissions declared in its
addon.json; - rolls back its migrations (this drops its database tables and every row they hold) and purges its entries from the
migrationstable, so a later reinstall migrates from scratch; - deletes its published assets:
public/addons/helpdesk/,public/vendor/helpdesk/andstorage/app/public/addons/helpdesk/; - deletes the add-on directory
extensions/addons/helpdesk/; - deletes its row in the
addonstable (the recorded purchase code goes with it) and clears the application cache.
Removal is refused, with an explanatory message and before anything is destroyed, when the add-on is still active, when another active add-on depends on it, or when the web server (PHP) user cannot delete extensions/addons/helpdesk/. In that last case, give that user write permission on the directory and on its parent, then try again.
Deleting the folder over FTP or SSH is not equivalent: the add-on's tables, its entries in the migrations table and its addons row are all left behind, and its card stays in the list. Use Remove in the admin panel instead.
Troubleshooting
Ticket creation form shows no departments
- Ensure at least one department exists and is marked as active.
- If the Licenses add-on is active with
licenses_helpdesk_require_purchaseenabled, the user must hold an active license key for a product linked to that department.
Guest users cannot submit tickets
- Check that
helpdesk_guest_accessis set totruein settings. - The setting defaults to
truebut may have been disabled in the admin panel.
Custom fields not appearing on the ticket form
- Ensure the custom field is marked as active.
- Verify the field type is set correctly.
- For select/checkbox/radio fields, ensure the options array is populated.
Notifications not being sent
- Check that the master switch
notifications_enabledand the corresponding notification type (e.g.notification_helpdesk_new_ticket_admin) are enabled under Admin → Settings → Notifications, and that the recipient has not opted out in their own notification preferences. - Verify your mail configuration in
.env(SMTP, Mailgun, etc.) is correct. - Check the
failed_jobstable for queued notification failures. - For guest tickets, ensure the guest email address is valid.
AI Assistant returns errors
- Ensure at least one AI provider API key is configured in
.env(e.g.ANTHROPIC_API_KEY,OPENAI_API_KEY). - Check
config/ai.phpfor the default provider configuration. - Verify the
laravel/aipackage is installed (composer show laravel/ai). - Check server logs for detailed error messages from the AI provider.
KB articles not showing on the front-end
- Ensure articles have status set to
publishedand published_at is in the past. - Check visibility:
auth_onlyarticles are hidden from guests. - If
kb_require_authis enabled, unauthenticated users are redirected to login (except onalways_publicarticles). - Ensure the article’s collection is marked as active.
PDF export fails
- Ensure the
barryvdh/laravel-dompdfpackage is installed. - Check that the
storage/appdirectory is writable by the web server. - If ticket content contains external images, ensure
isRemoteEnabledis true (it is by default).
Ticket merge fails
- You cannot merge a ticket into itself: the source and target must be different tickets.
- Ensure you have the
helpdesk.tickets.editpermission. - Check server logs for database transaction errors.
Helpful votes not working
- Ensure
kb_show_helpful_votesis enabled in KB settings. - The vote endpoint (
POST /support/vote/{id}) returns JSON: verify that JavaScript is handling the AJAX call correctly. - Check the browser console for network errors.
Inbound webhook reports Fetched 0 even though the mailbox has messages
- Normal polls only fetch unread messages. Already-read replies (for instance those you pre-opened in a mail client) are skipped by design.
- To ingest already-read history, add
?backfill=1to the webhook URL or runphp artisan helpdesk:fetch-inbound --backfill. Duplicates are blocked automatically by the raw-body hash.
Inbound webhook returns 404
- The token must be the full value from
helpdesk_fetch_inbound_token(32+ alphanumeric characters). Regenerate it from the Email Bridge settings if it leaked. - Tokens are compared with
hash_equals(): even a trailing space causes a mismatch.
Ingested replies appear with the wrong timestamp (all grouped at the time of import)
- The ingestion pipeline uses each message’s
Date:header ascreated_at. This fix requires v1.0.5 or later: older imports keep the ingestion time. - To re-import an affected thread: delete the mistimed replies from the ticket and re-run a backfill:
php artisan helpdesk:fetch-inbound --backfill.
IMAP connect keeps failing with “Connection refused”
- Some providers (notably OVH) hold the previous session open for a few seconds. Increase
helpdesk_imap_connect_retries(e.g. to2or3) andhelpdesk_imap_connect_backoff_seconds(e.g.5) in Email Bridge settings. - On hosts whose CLI path cannot open port 993 at all, use the inbound webhook driven by an external scheduler.
License gating not restricting access
- Ensure the Licenses add-on is installed and active.
- Enable
licenses_helpdesk_require_purchase(orlicenses_kb_restrict_by_purchasefor the Knowledge Base) under Admin → HelpDesk → Settings → Licenses Integration. - Link at least one product to the department or KB collection you want to restrict. Entities with no linked product are always unrestricted.
HelpDesk & Knowledge Base v1.0.0: Part of the Larapen CMS platform.
© BeDigit. All rights reserved.