expert Helpdesk
Turns mailboxes into a helpdesk. Incoming mail becomes a ticket, customer replies land on the ticket that already exists, and your agents answer from the ticket page — inside Redmine, in the customer’s own mail thread.
Most Redmine helpdesk setups break in the same place: a customer replies, and the reply becomes a second ticket. This plugin hands every incoming message to Redmine’s own MailHandler, so In-Reply-To headers and [#id] subject patterns behave exactly as they do for Redmine’s built-in mail handling — attachments included. Everything else is layered on top of that.
What it does
Mail in
A mail arrives, a ticket appears
Each project points at its own mailboxes and sets what new tickets get: tracker, priority, status, and what to do about senders who have no Redmine account. The original message is archived on the ticket as a .eml attachment, and the signature logos and screenshots that mail clients reference as [cid:image001.png] are rewritten to point at the attachment Redmine just stored — so the ticket reads like the mail did, not like a row of markers. When the mail also went to other people, a To +1 | CC +2 pill next to the sender opens every To and CC address, with this mailbox marked as such — so you see at a glance whether the customer wrote to three support addresses at once or copied colleagues in. Replies in the history carry the same pill.
Mail out
Answer the customer from the ticket
The normal Redmine note field, plus a panel that sends it as a real mail through the mailbox itself — so a copy lands in Sent and the mailbox holds both halves of the conversation. Quote the original mail or the whole thread (private notes never among them), insert a stored answer template with its macros already filled in, drop in inline images, and autocomplete To/CC/BCC from the project’s contacts. Whoever the customer had in To or Cc is one click away. While the reply is switched on, the whole block wears a hazard border in a colour you choose, so nobody mistakes it for an internal note.
AI drafts
A first answer from tickets you already solved
One button in the note toolbar drafts the reply from the project’s knowledge base — as a plain answer, step-by-step instructions, a short one, or a polite request for the details that are missing. The draft names the resolved tickets it is based on, and without a hit above the relevance threshold it refuses rather than guessing. Sending a draft nobody edited asks first. An optional cross-encoder reranks the knowledge-base hits before any of this. Off by default, like every AI feature here.
Attachments
Block the signature logo once, not on every ticket
Mail clients store every signature logo, icon and tracking pixel as an attachment, so the one screenshot that matters drowns among them. Block removes a file from every ticket in the project — after telling you how many copies that is — and keeps it off incoming mail from then on. The match is the file’s content (SHA-256), not its name: Outlook calls every embedded image image001.png, and blocking by name would sooner or later eat a real screenshot.
Any provider
Microsoft 365, or any IMAP server
Each mailbox picks its own backend: Microsoft Graph for Microsoft 365, or generic IMAP in and SMTP out for Google Workspace, Exchange on-premises, Dovecot, Zimbra and ordinary hosters. Authentication is OAuth2/XOAUTH2 with three grants — application-only, one-time consent, and service account — with username and password over TLS still available where there is no OAuth2. A Test connection button checks host, TLS and login before you save, and lists the folders it can see.
SLA
Deadlines in business minutes, not wall-clock hours
Reaction and solution targets are measured against the project’s own working days and hours, with per-priority overrides for the tickets that cannot wait. Both clocks show as traffic-light chips on the ticket and as sortable columns in the issue list, so a queue can be ordered by urgency at a glance. Breaches notify an escalation address through a cron endpoint, and an SLA statistics tab charts compliance over time.
Reporting
Where the time actually goes
A ticket statistics tab for every helpdesk project, SLA or not, behind its own permission so you can give it to a team lead and nobody else. Volume, first response and resolution times, time spent in each status, reopens, conversation length, a table per agent and a table per customer, busiest hours and weekdays — on calendar time, or on the project’s business hours.
Customers
Every sender becomes a contact
Senders are saved automatically and listed per project with company, phone and ticket count. The ticket page gets a customer card with their earlier tickets and the replies already sent, the issue list gets sortable Customer and Customer email columns with a filter matching either, and any manually created ticket can have a contact attached — optionally sending the first outbound mail as you do it.
Optional
AI summaries and a knowledge base, off by default
A hard-to-read forwarded thread can be summarised into a private internal note by OpenAI, Anthropic, or any OpenAI-compatible endpoint you host yourself. Resolved tickets can be distilled into a per-project knowledge base — Qdrant or pgvector — and new mail retrieves solutions from it, with strict isolation so one project never sees another’s. A separate completeness check mails the customer for the details a one-line “printer is broken” ticket left out. All of it ships disabled, all of it runs in background jobs, and the AI paths fail closed.
Knowledge base
Correct what the knowledge base learned
An extracted problem and solution is only as good as the ticket it came from, and fixing the text inside the vector store does not help — the vector stays the one computed from the old words. A Knowledge base tab per project lists the entries with a status filter and full-text search; editing an approved entry re-embeds it on the spot. Entries can be approved, rejected, written by hand for a ticket, or deleted, and the project’s index rebuilt in the background, on Qdrant and pgvector alike. A person’s verdict sticks: reopening and closing the ticket again does not overwrite a corrected or rejected entry. Three permissions separate reading, editing and managing.
Switching over
Coming from RedmineUP without losing the history
If the database still holds the tables of RedmineUP’s redmine_contacts and redmine_contacts_helpdesk, the plugin settings offer to import the contacts with the customer link of old tickets, and to move the original mails off RedmineUP’s own ticket onto the Redmine issue, where this plugin shows them. Both work per project — the selection page shows how many contacts or mails each one would touch — and run as a background job with a progress page, because a real dataset outlasts any browser timeout. For a project that still works with the RedmineUP helpdesk, Give back to RedmineUP moves its mails back.
Why this one
- Coexists with what you already run. Two core helpers are patched by capturing the original method rather than
prepend/super, specifically so the plugin survives alongside older plugins that extend the same helpers withalias_method_chain. - Reply matching is not reinvented. Threading is Redmine’s
MailHandler, so it behaves the way the rest of your Redmine already does. - Nothing silently reprocesses. Ingested mail moves to a processed folder, rejected mail to skipped, errors to failed. One broken message never aborts a fetch run.
- Secrets are encrypted at rest and write-only over the REST API — you can set a client secret, you can never read one back.
- No scheduler of its own. Fetching is a button, or an API-key-secured endpoint you point cron or a Kubernetes CronJob at. Your scheduler stays in charge.
- Upgrades without cutting a mail in half. A maintenance switch — or one API call from your upgrade script — pauses every fetch, and a fetch already running stops before its next message. A status endpoint reports
safe_to_stoponce no pod in the cluster is inside a fetch; unprocessed mail simply waits in the mailbox. - Full REST API, JSON and XML, following Redmine’s own conventions and scoped by the permissions you already granted.
Testers wanted
Try it, break it, tell us
These plugins are young and move quickly. The suites run on every Redmine from 5.1 to 7.0, but our own installation is only one setup — we would like to hear from people who run them somewhere else: another mail server, another theme, next to other plugins, or with a workflow we never thought of. Something broken, something missing, something that works but feels wrong: all of it helps.
The bug form asks for the plugin version and your environment; pasting the table from Administration → Information covers the rest. To hear about new versions, watch the repository on GitHub for releases.
Compatibility
- Latest release
- v0.24.2 2026-11-04
- Redmine
- 5.1 · 6.0 · 6.1 · 7.0
- Database
- MariaDB, MySQL
- Additional gems
- base64
- Migrations
- 62
- Licence
- GPL-2.0-or-later
Requires MariaDB or MySQL. The SLA columns in the issue list compare
against UTC_TIMESTAMP(), which PostgreSQL does not have; CI runs on MariaDB
only, so PostgreSQL is untested here. The other three plugins are
database-agnostic.
base64 is declared as a plugin gem — it stopped being a Ruby default gem in
Ruby 4.0, which is what Redmine 7 runs on. pdf-reader is optional and only
used to extract text from PDF attachments for AI summaries; without it those
attachments are simply skipped. The pgvector knowledge-base backend needs
pg in your deployment, which the default Qdrant backend does not.
Installing
- Download the release archive: redmine_expert_helpdesk-0.24.2.zip
- Unpack it into your Redmine's `plugins/` directory:
cd /path/to/redmine unzip redmine_expert_helpdesk-0.24.2.zip -d plugins/ - Run the migrations:
bundle exec rake redmine:plugins:migrate NAME=redmine_expert_helpdesk RAILS_ENV=production - Restart Redmine, then enable the **expert Helpdesk** module on a project and add a mailbox in the project's settings.
Mail provider setup — the Microsoft 365 app registration, or OAuth2 against your own IMAP server — is the one part that takes real configuration. The README has recipes for Microsoft application-only IMAP, Google Workspace consent and self-hosted servers, plus PowerShell scripts for the Azure side.