Plugins Directory » Redmine expert Helpdesk
|
|
Author: | Dennis Buehring |
|---|---|---|
| Website: | https://github.com/expertZentrale/redmine_expert_helpdesk | |
| Code repository: | https://github.com/expertZentrale/redmine_expert_helpdesk | |
| Registered on: | 2026-07-29 (about 2 months ago) | |
| Current version: | 0.10.0 | |
| Compatible with: | Redmine 7.0.x, 6.1.x, 6.0.x, 5.1.x | |
| User ratings: |
What it does¶
Turns mailboxes into a Redmine helpdesk. Incoming mail becomes a ticket, replies are matched onto the existing ticket, and agents answer the customer straight from the ticket page. Contacts, SLA tracking, phishing detection and a rules engine sit on top.
Reply matching is delegated to Redmine's own MailHandler, so In-Reply-To and [#id] subject patterns behave exactly as they do for Redmine's built-in mail handling, attachments included.
Mail providers¶
Each mailbox chooses its own backend:
- Microsoft 365 via the Microsoft Graph API (application-only).
- Generic IMAP/SMTP for Google Workspace, Exchange on-premises, self-hosted servers (Dovecot, Zimbra) and any ordinary hoster.
Authentication is OAuth2/XOAUTH2 by default, with three grants — client_credentials (app-only), authorization_code (one-time consent) and jwt_bearer (service account with domain-wide delegation). Username/password over TLS remains available for servers without OAuth2. Per-mailbox secrets are stored encrypted.
Features¶
- Per-project mailboxes — source and target folders, defaults for tracker, priority and status, handling of unknown senders.
- Autoresponder — configurable confirmation mail for new tickets.
- Customer replies from the ticket page, with header/footer templates, inline images (CID), attachments and CC/BCC. Sent through the mailbox itself, so a copy lands in its Sent folder. A mailbox routed over Redmine's own SMTP settings can optionally send under a different address, for a distribution list that became a helpdesk mailbox.
- Address autocomplete — To/CC/BCC suggest matching project contacts.
- Quoting and answer templates — next to the formatting icons of the note field, a Quote button inserts the original mail, the complete conversation or only the mail exchange (private notes never among them), and a Templates button inserts a stored answer with its macros already resolved. Templates are maintained globally and per project.
- Template macros — ticket fields (status, priority, tracker, author, assignee, category, version, dates, progress), the replying agent's name, login and address, and the project. Issue custom fields can be used too, each one enabled by an administrator and still subject to its own Redmine visibility, so an internal field cannot reach a customer through a shared template.
- Embedded images — inline images of an incoming mail are shown in the ticket where the mail showed them, instead of leaving a
[cid:…]marker behind, and images referenced in a reply are sent to the customer as inline parts. - Contacts — senders are saved automatically; customer list per project, a customer panel with previous tickets on the ticket page, and sortable Customer / Customer email issue-list columns with a filter matching name or email.
- Black-/whitelists — sender and domain filters per mailbox.
- SLA — reaction and solution targets in business minutes, per-priority overrides, traffic-light display, sortable and filterable SLA columns in the issue list, breach notification via a cron endpoint, plus an "SLA statistics" tab with charts.
- Phishing detection — optional check of incoming links against a local mirror of PhishTank and the Phishing.Database feed, Microsoft SafeLinks decoded locally. On a hit the links are neutralised or the mail is quarantined, per project.
- AI summaries and knowledge base (optional) — per-project mail summaries via OpenAI, Anthropic or an OpenAI-compatible endpoint, and a RAG knowledge base built from resolved tickets (Qdrant or pgvector). Off by default.
- Awaiting response — a ticket is flagged when a customer replies by mail or a reply reopens a closed ticket, and unflagged when an agent answers publicly or closes it. Sortable column and filter in the issue list, a marker on the row, a sidebar counter and a My page block.
- Ticket assignment — new tickets from mail go to a user or a group chosen per project, a matching rule overrides that for the mail it applies to, and the "assign to me after reply" option claims only a ticket nobody holds yet.
- Rules — automation by subject or sender: set priority, tracker, category, assignee (user or group), or ignore the mail.
- Initial mail — assign a customer contact to any ticket and optionally send the first outbound mail.
- REST API (JSON and XML) following Redmine's own conventions, authenticated with
X-Redmine-API-Keyand scoped by the existing permissions: contacts, tickets, per-project settings and mailboxes. Mailboxes are fully configurable over the API — backend, IMAP/SMTP hosts, OAuth2 credentials, folders — with atest_connectioncall to verify one. Secrets can be written but are never returned.
Requirements¶
- Redmine 5.1.x, 6.0.x, 6.1.x or 7.0.x
- No additional gems — Ruby standard library only
The pgvector knowledge-base backend is the one exception and needs gem 'pg' in your deployment; the default Qdrant backend does not.
Links¶
License¶
GNU General Public License, version 2 or later — the same license Redmine itself uses.
Installation notes
Requirements¶
- Redmine 5.1.x, 6.0.x, 6.1.x or 7.0.x
- No additional gems — Ruby standard library only
Every release is tested against Redmine 5.1, 6.0, 6.1 and 7.0 on a clean MariaDB, and additionally booted inside the official redmine Docker images for the same versions.
The only exception to "no gems" is the optional pgvector backend of the knowledge base, which needs gem 'pg' in your deployment. The default Qdrant backend does not, and both are off unless you enable them.
1. Install the plugin¶
Download redmine_expert_helpdesk-<version>.zip (or .tar.gz) from the Releases page. The archive already contains a top-level redmine_expert_helpdesk/ folder, so extract it directly into your Redmine plugins/ directory:
cd /path/to/redmine/plugins unzip redmine_expert_helpdesk-<version>.zip
Alternatively, from source:
cd /path/to/redmine/plugins git clone https://github.com/expertZentrale/redmine_expert_helpdesk.git
The directory must be named redmine_expert_helpdesk — Redmine derives the plugin id from it, and the plugin's own asset and settings paths depend on it.
2. Run the migrations¶
cd /path/to/redmine bundle exec rake redmine:plugins:migrate NAME=redmine_expert_helpdesk RAILS_ENV=production
Then restart Redmine so the plugin is loaded — touch tmp/restart.txt for Passenger, or restart the Puma/Unicorn service.
3. Enable the module and grant permissions¶
Enable expert Helpdesk per project under Project settings → Modules, then assign the permissions under Administration → Roles and permissions:
| Permission | Grants |
|---|---|
| Manage helpdesk mailboxes | Mailbox configuration, folders, rules |
| Fetch helpdesk mails | The "Fetch mails now" button |
| Send customer replies | Reply form, contact autocomplete, initial mail |
| View helpdesk info | Info bar and sidebar on the ticket page |
| Manage contacts | Customer list and customer profile |
| View SLA statistics | The project's "SLA statistics" tab |
View AI statistics is the one global permission — it is granted once to a role and then applies across all projects without project membership, because it exposes the token spend of the AI features. The tab it unlocks is shown only where the AI features or the knowledge base are enabled; with both switched off it stays hidden and the page answers 403.
4. Central configuration¶
Under Administration → Plugins → Redmine expert Helpdesk, or via the expert Helpdesk entry in the Administration menu, which links there directly.
| Setting | Purpose |
|---|---|
| Tenant ID / Client ID / Client Secret | The Azure app registration, used by Microsoft 365 mailboxes and shared by IMAP/SMTP mailboxes that authenticate with OAuth2 |
| Default credentials for IMAP/SMTP | Preset, grant, authorization and token URL, scope, plus default IMAP/SMTP host, port and encryption |
| API key (mail fetch) | Secures the global helpdesk/fetch_all endpoint |
| API key (SLA check) | Secures the helpdesk/sla_check endpoint |
| Entries per page | Page size of the customer list (default 25) |
| Max. tickets in customer profile | Tickets shown in the customer detail view (default 10) |
| Mark tickets awaiting a response | Flags tickets whose customer replied by mail and clears the flag when an agent answers (on by default) |
| Answer templates | Global canned responses offered in every helpdesk project, in addition to a project's own ones |
| Embedded images | Rewrites the [cid:…] markers of incoming mails to the stored image attachment (on by default) |
| Custom fields as macros | Issue custom fields that may be used as {{issue.cf.…}} in templates. Nothing is enabled by default; a field's own Redmine visibility still applies on top |
Nothing here is required for a mailbox that carries its own credentials — see the next step.
5. Add a mailbox¶
Under Project settings → Helpdesk, add a mailbox and pick its Mail provider:
| Provider | Incoming | Outgoing | Typical use |
|---|---|---|---|
| Microsoft 365 (Graph) | Graph API | Graph sendMail |
Microsoft 365 / Exchange Online |
| IMAP / SMTP | IMAP | SMTP | Google Workspace, Exchange on-premises, Dovecot, Zimbra, any hoster |
Credentials is an explicit switch, not a fallback chain. From plugin settings uses the central app registration above; Individual for this mailbox uses only the mailbox's own fields. A mailbox uses one source entirely — blank fields are deliberately not filled in from the other, so a half-configured mailbox cannot silently authenticate against the wrong tenant.
Use Test connection before saving. It performs a real login, lists the folders and probes the outgoing route, so a wrong port, encryption mode or credential surfaces immediately instead of on the first fetch.
Reply transport decides how outgoing mail leaves: through the mailbox's own backend, through Microsoft Graph, or through Redmine's global SMTP settings. Only the last one offers Sender override (From), an optional address to send under instead of the mailbox address — meant for a distribution list that became a helpdesk mailbox. The relay must be allowed to send for that address. Graph and the mailbox's own SMTP server authenticate as the mailbox and reject a foreign sender, so the field is hidden for them.
For Microsoft 365 the app registration needs the application permissions Mail.ReadWrite and Mail.Send, granted with admin consent.
6. Schedule the fetch¶
There is no built-in scheduler. Mail is fetched either with the Fetch mails now button in the project settings, or through an endpoint meant for cron:
curl "https://redmine.example.com/helpdesk/fetch_all?key=FETCH-API-KEY"
That processes every active mailbox across all projects and piggybacks the phishing-feed sync. The SLA breach check is deliberately separate, so it can run on its own schedule:
curl "https://redmine.example.com/helpdesk/sla_check?key=SLA-API-KEY"
Both keys come from the central configuration. Endpoints without a key configured refuse the request rather than running unauthenticated.
REST API¶
The plugin's own REST API (contacts, tickets, project settings, mailboxes) is separate from the two cron endpoints above and uses Redmine's normal API authentication. Enable Administration → Settings → API → Enable REST web service; requests then authenticate with an X-Redmine-API-Key header and are authorized by the same permissions as the UI. Mailbox endpoints require manage_helpdesk for reading as well as writing, because the configuration exposes mail hosts, usernames and OAuth client ids. Full reference: API.md
Upgrading¶
Replace the plugin directory with the new version, then run the migration command from step 2 again and restart. Migrations are additive and existing mailboxes keep their configuration; if a release needs anything beyond that it is called out in that version's notes.
Downgrading is not supported — roll back the database alongside the plugin if you need to.
Uninstalling¶
bundle exec rake redmine:plugins:migrate NAME=redmine_expert_helpdesk VERSION=0 RAILS_ENV=production rm -rf plugins/redmine_expert_helpdesk
This drops the plugin's tables and all helpdesk data — contacts, message log, SLA tracking. Tickets created from mail are ordinary Redmine issues and stay.
Changelog
0.10.0 (2026-09-14)
Compatible with Redmine 7.0.x, 6.1.x, 6.0.x, 5.1.x.
Added¶
- A separate status for a reply to an open ticket. A mailbox had one status for inbound customer replies, and it only ever applied to a closed ticket — that assumes the ticket is closed while the helpdesk waits for the customer. Where a ticket instead stays open under a status such as waiting for customer or on hold, the reply had no way to move it on. Helpdesk → Mailbox → Status on customer reply now offers two fields: Status (closed issue), the previous reopening status, and Status (open issue), applied when the mail lands on a ticket that is still open. The ticket's state when the mail arrives decides which one is used. Both are optional and independent of each other. Like the reopening, the change is written onto the same history entry that carries the customer's reply, so the ticket history shows one entry and nobody is notified twice. Readable and writable over the REST API as
open_reply_status_idonhelpdesk_mailbox.
Upgrade notes¶
Migration 051 adds one column, open_reply_status_id on helpdesk_mailboxes. Run the usual redmine:plugins:migrate after replacing the plugin directory.
No behaviour changes without configuration: the new field is empty on every existing mailbox, and an empty field leaves the status of an open ticket alone, exactly as before. The reopening status and its age limit are untouched.
One detail worth knowing if you read the ticket statistics: only a closed ticket that ends up in an open status counts as a reopening, both for the Reopened figures and for the Reopened label on the awaiting-response flag. A status change on an already open ticket is an ordinary reply, and so is a reopening status that is itself closed — it is applied, the ticket simply never becomes open.
0.9.3 (2026-09-13)
Compatible with Redmine 7.0.x, 6.1.x, 6.0.x, 5.1.x.
Fixed¶
- The "Reply" button did not preselect the status or the assignee. Ticking Send as email to customer by hand applied the status configured under Project settings → expert Helpdesk → Reply settings and, if enabled, assigned the ticket to you. The Reply button above the ticket — which ticks that very box for you — did not: it set the checkbox from script, which fires no
changeevent, and the preselection is what hangs off that event. The mail fields opened, but the ticket kept its old status and stayed unassigned, so an agent replying through the button silently lost both settings. Both routes now go through the same handler and end in the same place.
Upgrade notes¶
No migrations, no configuration changes and no new settings. The behaviour is what Reply settings already promised — if you configured a reply status or Assign ticket to me when replying and only ever saw it apply when you ticked the checkbox yourself, it now applies via the button too. Replace the plugin directory and restart.
0.9.2 (2026-09-13)
Compatible with Redmine 7.0.x, 6.1.x, 6.0.x, 5.1.x.
Fixed¶
- The documentation said the plugin needs no gems. It does. Both READMEs and this listing stated "no additional gems — Ruby standard library only" in six places, which stopped being true when
PluginGemfilegainedbase64andpdf-reader.base64is required: it is no longer a Ruby default gem as of Ruby 4.0, and the officialredmine:7Docker image ships Ruby 4.0, so the plugin does not load there without it.pdf-readeris optional and only extracts text from PDF attachments for the AI summaries; the call is guarded, so a missing gem just means those attachments are skipped. Anyone sizing up a deployment from the README was told they needed nothing and would then have hit a missingbase64on Redmine 7. - The installation steps never ran
bundle install. They went from unzipping the archive straight to the migration, which fails on Redmine 7 for exactly the reason above. Redmine picks a plugin'sPluginGemfileup on its own, so there is nothing to add to your deployment's ownGemfile— but the bundle has to be installed before the migration runs. - The release workflow accepted tags that are not versions. Its semver check let a fourth numeric component through (
v1.2.3.4), accepted empty identifiers (1.2.3-a..b) and allowed leading zeroes (v01.2.3,v1.2.3-01). Any of those would have been packaged and published as a release.
Upgrade notes¶
No migrations, no configuration changes and no behaviour changes — this release is documentation and one workflow. Replace the plugin directory and restart. If you are installing rather than upgrading, note the corrected step order: unzip, bundle install, rake redmine:plugins:migrate, restart.
0.9.1 (2026-09-11)
Compatible with Redmine 7.0.x, 6.1.x, 6.0.x, 5.1.x.
Changed¶
- "Status when replying" is a preselection now, not a rule. The status configured under Project settings → expert Helpdesk → Reply settings used to be written into the ticket the moment the mail went out, one step before the form was saved — every reply landed on it, and the agent never saw the decision, let alone got to change it. It is now filled into the ticket's own status field as soon as Send as email to customer is ticked, while the reply is still being written. A status the agent picks instead is kept; unticking the box restores the original one only while they have not picked anything themselves. That difference matters for the answer that ends in a question: a ticket waiting on the customer belongs out of the list of open tickets, in a status such as Feedback closed, for however many weeks the answer takes — or forever, if it never comes. Assign ticket to me when replying was moved to the same moment and is just as reversible.
Fixed¶
- A reply no longer blanks the status field when the workflow forbids the configured status. The status set in the reply settings is not filtered by the workflow, so it is easy to configure one the agent's role may not move the ticket to. The old code assigned it anyway; with no matching entry in the status list the field simply went empty, and the reply was saved with whatever Redmine made of that. The configured status is now skipped when the workflow does not offer it, and the ticket keeps the status it had.
- The preselection survives editing other ticket fields. Changing status, tracker or priority makes Redmine re-render the attribute block of the form. The reply block held on to the old fields across that and wrote into elements that were no longer on the page, so the preselection quietly stopped working for the rest of the edit.
Upgrade notes¶
No migration and no configuration change. The setting, its database column and its REST API field are unchanged, and existing configurations keep working — what changed is when and how the value is applied: it is offered to the agent instead of being written for them. Anything reading reply_status_id over the REST API should note that it now describes a preselection in the ticket form, not a value the plugin applies after a reply; API.md has been updated accordingly.
0.9.0 (2026-09-10)
Compatible with Redmine 7.0.x, 6.1.x, 6.0.x, 5.1.x.
Added¶
- "Never ask this customer for more information" per customer. The completeness check already had a per-project list of senders it must never follow up on. That list needs an administrator and an address known in advance — but the Veeam job report, the monitoring alert or the cron mail is usually recognised by whoever is looking at the ticket it just produced. There is now a flag on the customer itself, and the ticket header carries it: one click on never ask next to the customer name, and you are back on the ticket you were reading. The same flag is a checkbox on the customer form and writable through the REST API as
info_request_opt_outonhelpdesk_contact. Flagged customers are marked no follow-up in the ticket header and in the customer list, with ask again next to it — a follow-up that never went out therefore never looks like a broken check. Tickets from those senders are still created and still evaluated; only the mail to the customer is suppressed, in the rule-based and the AI mode alike. Setting the flag requires the Manage customers permission; the marker is visible to everyone.
Fixed¶
- "Never ask these senders" is editable in the AI mode again. The field sat in the rule-based part of the project settings, which the mode switch hides as soon as the check runs on AI. The list still applied — the check reads it in both modes — it was simply invisible and could not be maintained there, which is worse than a missing field: entries made in the rule-based mode kept working with nothing on screen to say so. The field has moved next to the follow-up templates, which both modes share.
Upgrade notes¶
One migration runs on upgrade (050). It adds info_request_opt_out to helpdesk_contacts with the default false, so no customer changes behaviour and no configuration is required: every customer keeps being asked exactly as before until somebody flags one.
The never ask link in the ticket header is shown to members with the Manage customers permission, and only while the completeness check is actually running in that project — no new permission is introduced.
0.8.1 (2026-09-09)
Compatible with Redmine 7.0.x, 6.1.x, 6.0.x, 5.1.x.
Added¶
- Sortable customer list and statistics tables. The project's customer list sorts by every column (name, email, company, phone, ticket count, last ticket) through the column headers, server-side and across pages and searches. The three tables of the ticket statistics (time in status, per agent, per customer) sort in the browser by clicking a header or pressing Enter/Space on it; durations and percentages sort as numbers, empty cells last.
Upgrade notes¶
No migration and no configuration change.
0.8.0 (2026-09-09)
Compatible with Redmine 7.0.x, 6.1.x, 6.0.x, 5.1.x.
Added¶
- "Ticket statistics" tab per project. A third statistics page next to SLA and AI statistics, available for every helpdesk project — with or without SLA — and unlocked by the new member permission View ticket statistics (meant for a "helpdesk manager" role; without it the tab is hidden and the page answers 403). It covers the helpdesk tickets created in the selected period: ticket volume, average and median first response and resolution, time in status (only statuses that actually occur, completed stays plus the tickets currently sitting there), reopens, tickets awaiting a response, conversation length (incoming mails per ticket, agent replies, automatic mails, share of tickets solved with a single reply), a per-agent table and a per-customer table, plus busiest hours and weekdays. Durations are calendar time; with SLA enabled a time-basis switch shows the same figures in the project's business hours. Charts use the bundled Chart.js, no CDN.
Changed¶
- The first reaction is recorded for every helpdesk ticket, not only under SLA. A public agent note or a customer reply mail sets the first-response timestamp regardless of the project's SLA switch, together with the acting user; only the business-minute figure, the deadlines, the breach mails and the SLA chips remain SLA features. Without SLA no ticket-info row is created for this — the timestamp only lands on tickets that already are helpdesk tickets.
Fixed¶
- SLA statistics no longer count reopened tickets as closed. Redmine keeps the closing timestamp after a reopen; the page now derives "closed" from the current status.
Upgrade notes¶
- Run the plugin migrations (
bundle exec rake redmine:plugins:migrate NAME=redmine_expert_helpdesk RAILS_ENV=production): migration 049 adds the column for the first-response user and backfills it from the note written at the recorded moment where one exists. - Grant the new permission View ticket statistics to the roles that should see the tab — it is not granted to anyone by default.
- No backfill of the first-response timestamp itself: tickets of projects without SLA that were answered before this version show no first-response figure in the statistics.
0.7.2 (2026-09-08)
Compatible with Redmine 7.0.x, 6.1.x, 6.0.x, 5.1.x.
Fixed¶
- The settings REST API writes the completeness-check fields.
PUT /projects/:id/helpdesk/settingsaccepted everyinfo_request_*key documented in the API reference without error and then ignored it — the response echoed the unchanged value and the caller only noticed by reading it back. All of them are applied now, partially like the other fields: mode, rule values, expected terms, sender list, threshold, prompt mode and prompt, follow-up subject and body, note visibility and the status after a follow-up. Two rules the project settings form silently corrects answer 422 on the API instead: a threshold below 1, and a status that is closed or does not exist. A closed status is refused because an automatic follow-up must never close a ticket — every SLA reader treats a closed ticket as both reaction-done and solution-done.
Upgrade notes¶
No migration and no behaviour change for the web UI. Scripts that already send info_request_* keys and relied on them being ignored will now change the project's settings — check them before upgrading.
0.7.1 (2026-09-08)
Compatible with Redmine 7.0.x, 6.1.x, 6.0.x, 5.1.x.
Fixed¶
- The completeness check reads the subject line. Both modes evaluated the mail body alone, so a ticket opened as "Drucker HP 4050 im EG druckt nicht" with a one-line body was asked which device was affected — the customer had already said so in the subject. The subject now counts towards the rule-based length and expected-term checks and is shown to the AI model as the first line of its input (
Betreff: …), with the default prompt told to treat it as information. It is joined after the quoted-history stripping, so a subject that looks like a forwarded header is never cut, and in AI mode it is added after the body truncation, so a long thread cannot push it out.
- The AI summary and the knowledge base read the subject line too. The summary job built its model input from the body alone, so an informative subject over a one-line body was skipped as "too short" or summarised without the device and error the subject named; the knowledge extractor embedded the same tickets without it. Both now start the text with
Betreff: …, the subject counts towards the minimum input length, and both default prompts explain the line. The archived.emlis also parsed only once per summary run instead of up to three times.
Upgrade notes¶
No migration. Two behaviour changes worth knowing:
- Rule-based completeness thresholds (minimum characters / words, expected terms) are now met by subject and body together. A project that tuned these values to the body alone may see fewer follow-ups; lower the values if that is unwanted.
- Minimum input characters for the AI summary now include the subject, so some mails that were previously skipped as too short are summarised.
0.7.0 (2026-09-03)
Compatible with Redmine 7.0.x, 6.1.x, 6.0.x, 5.1.x.
Added¶
- The AI summary no longer pays vision tokens for signature logos. With Attachments → Images enabled, every image of a mail was sent to the model — including the logos, social icons and tracking pixels that hang off nearly every business mail. With only four image slots per request, a three-image signature could push the actual screenshot out of the batch entirely. Images are now filtered first: below the new per-project minimum image size (default 15 KB,
0disables it), below 64x64 pixels, or embedded inline and byte-identical to an image on other tickets — a signature graphic, whereas a pasted screenshot is inline but unique. Every stage keeps the image when its signal is unavailable (unknown size, unreadable header, unknown format), so the filter can only lose the logo, never the screenshot. Image dimensions are read from the file header, so no additional gem and no ImageMagick is required.
- The completeness check no longer asks robots for more information. Veeam job reports, cron mails and monitoring alerts are legitimate tickets, but nobody behind them can answer a follow-up — the request bounced or, with a chatty mailer, looped. Two gates now run before the rule evaluation, so the AI mode spends no token on them either: the new per-project "Never ask these senders" list (one entry per line: full address, bare domain or
"@domain"), and automatic detection of mail that marks itself machine-generated in its headers (Auto-Submitted,Precedence: bulk|list|junk,X-Auto-Response-Suppress,X-MS-Exchange-Generated-Message-Sourceand theX-Autoreply/X-Autorespond/X-Autoresponderheaders). Delivery failures (NDR) are deliberately excluded — a bounce carries the same headers but is something the plugin has to act on.
Changed¶
- A two-line mail with a signature logo is summarized no more. The "too short to be worth an AI call" short-circuit only fires when no image is attached, so any signature logo defeated it and sent the mail to the provider anyway. With the logos filtered out beforehand, the skip works as documented. Filtered images are reported in the AI debug log.
- Machine-mail detection and the image size test each live in one place now. The header list was previously known only to the incoming-mail processor, which uses it to drop auto-replies before they become tickets; the completeness check needs the same answer to "did a human write this" for the opposite reason. The image size floor was previously known only to the completeness check. Both are shared now, so the features cannot drift apart.
Upgrade notes¶
Two migrations run on upgrade (047, 048). Both add a per-project setting with a safe default, so no configuration is required:
- Minimum image size defaults to 15 KB. If a project relies on the AI summary seeing very small images, set the value to
0in Project → Settings → expert Helpdesk to switch the floor off. Images smaller than 64x64 pixels are skipped regardless of this value. - "Never ask these senders" starts empty. The header-based detection is active immediately, so tickets from mailers that identify themselves as automated stop receiving follow-ups even without an entry.
Both settings are exposed through the REST API (ai_min_image_kb, info_request_sender_blacklist).
0.6.0 (2026-09-02)
Compatible with Redmine 7.0.x, 6.1.x, 6.0.x, 5.1.x.
Added¶
- Incoming mail is checked for sufficient information, and the customer can be asked for the rest automatically. A ticket that arrives as "the printer is broken" — no screenshot, no error message, no system name — costs an agent the first cycle just to write "please tell us more", and that cycle runs against the SLA. The plugin now evaluates the first mail of a new ticket and, when it does not carry enough to start working, sends the customer a templated follow-up listing what is missing, records a journal note, and can move the ticket to a "waiting for customer" status. Off by default.
- Two modes, chosen per project. Rule-based works without AI: minimum length in characters and/or words, "an attachment is required", a minimum image size, a list of expected terms, and a threshold saying how many of those rules must fail before the customer is asked. AI-powered lets the model return a verdict plus the concrete details it found missing, which go straight into the follow-up mail; it uses the AI provider already configured for the summaries and appears in the AI statistics as its own
completenessrequest type.
- The AI prompt asks for the right kind of evidence. A screenshot when the problem is software (an application, a web portal, an error dialog) and a photo when it is hardware (a device, printer, till, display, cabling — ideally showing the type plate or the display). When it cannot tell which of the two is meant, it asks for the affected system instead of guessing. The model is also told which files are already attached, so it never asks for a screenshot the customer has already sent.
- Quoted history and signature logos do not count as content. Quoted conversations, forward headers and signatures are stripped before anything is measured, so a two-word reply under a long thread does not pass as a detailed report. Images below a configurable size (15 KB by default) are ignored as evidence, because signature logos and tracking pixels hang off nearly every mail and would otherwise satisfy "an attachment is required" every single time. That size floor applies to images only — a small log file or PDF is evidence of a different kind and still counts.
- Editable texts. Subject and body of the follow-up are templates using the usual macros plus
{{missing_info}}, which inserts the rendered list of missing details. They are configured centrally and can be overridden per project, as can the check prompt (inherit, extend or replace, exactly like the summary prompt). The journal note recording the follow-up is public by default — the customer received the same text by mail — and can be switched to an internal note per project.
Fixed¶
- An automatic note can no longer clear the "awaiting response" flag. The plugin writes several notes by itself — the autoresponder, the phishing warning and the new follow-up — all authored by the anonymous user. Clearing the flag was gated on the author holding Send helpdesk reply, which is normally enough, but a project that granted that permission to the Anonymous role turned every one of those notes into "an agent has answered". The phishing note was the sharp edge: it runs moments after the flag is set during the same mail fetch, so a waiting ticket disappeared from the queue immediately. Anonymous is now excluded outright; real agent replies are unaffected.
Upgrade notes¶
Four migrations run on upgrade (043–046), all of them adding optional columns to the helpdesk project settings and ticket info tables. Nothing needs to be reconfigured and no existing behaviour changes.
The completeness check is off after the upgrade and stays off until you switch it on twice: once centrally under Administration → Plugins → Redmine expert Helpdesk → Enable completeness check, and once per project under Project settings → expert Helpdesk, where the mode starts at Off. While the central switch is off, no project runs the check regardless of how it is configured.
It is deliberately conservative, because a wrong follow-up annoys a customer who wrote everything correctly:
- Only the first mail of a new ticket is ever checked. A reply in a running conversation never triggers a follow-up.
- Each ticket is asked at most once. The follow-up is claimed before the mail is sent, so a repeated mail fetch, a reopened ticket, a retried job or a manual re-run cannot mail the same customer twice.
- The AI mode fails closed: an unreadable model answer, an API error, or a verdict of "incomplete" without a single reason all count as complete, so no mail goes out.
- The check runs after the mail has been processed and can never break the mail fetch.
The follow-up does not touch the SLA. The note does not stop the reaction clock, and the optional status change can never close the ticket — every SLA evaluation treats a closed ticket as both the reaction and the solution, so closing here would mark both clocks met before the customer had even answered. Closed statuses are therefore not offered in the status selector. If your "waiting for customer" status is flagged as closed in Redmine, pick an open status for this setting or leave it empty.
The AI mode sends the mail body and the attachment names to the configured AI provider, exactly like the existing summaries. It needs no additional configuration beyond the AI settings that are already there, and the rule-based mode needs no AI at all.
0.5.1 (2026-09-02)
Compatible with Redmine 7.0.x, 6.1.x, 6.0.x, 5.1.x.
Added¶
- Reply templates, footers and autoresponders understand far more macros. Besides
{{issue.id}},{{issue.subject}}and{{issue.url}}a template can now use the ticket's status, priority, tracker, author, assignee, category, target version, start and due date, creation and update date, progress, description and parent id — 26 macros in total. The short forms of the original seven ({{ticket_id}},{{contact_name}}and friends) keep working unchanged.
- Macros for the replying agent.
{{user.firstname}},{{user.lastname}},{{user.login}}and{{user.mail}}join the existing{{user.name}}, so a signature can be built from the agent actually sending the mail.{{project.identifier}}was added next to{{project.name}}.
- Issue custom fields as macros, enabled per field. An administrator picks the fields under Administration → Plugins → expert Helpdesk → Custom fields as macros; only those expand. Each enabled field is addressable either by id (
{{issue.cf.42}}, which survives renaming the field) or by its name ({{issue.cf.vertragsnummer}}, easier to read in a template). Redmine's own field visibility still applies on top: a field the replying agent may not see renders empty, so an internal field cannot reach a customer through a shared template.
- Optional sender override for mailboxes that send through Redmine's SMTP configuration. A mailbox whose reply transport is SMTP (Redmine default) can send under an address other than the mailbox address. The case this serves is a distribution list that was turned into a helpdesk mailbox: the list address still exists and the mailbox is its only member, so mail should go out under the list address. Setting
Reply-Toto the mailbox address is a separate option and off by default, because for that setup the list address already delivers back to the mailbox and aReply-Towould only expose the internal address. The override is ignored for the Graph and own-SMTP transports, which authenticate as the mailbox and reject a foreign sender.
Fixed¶
- The macro list no longer drifts apart from what the renderer understands. The clickable macro chips read their list from the renderer itself instead of a separately maintained copy, and offer the common macros directly with the rest behind a "more macros" expander.
Upgrade notes¶
Two migrations run on upgrade (smtp_from_address and smtp_reply_to_mailbox on the mailboxes table). Both are optional columns with empty defaults, so every existing mailbox keeps sending exactly as before and nothing needs to be reconfigured.
Custom field macros are off until an administrator enables individual fields — an upgrade alone exposes no custom field in any template. On an installation that has saved the plugin settings before, the new setting starts out empty, which means no custom field macros.
The sender override only applies to the SMTP (Redmine default) transport. If you use it, the relay configured in Redmine's configuration.yml must be permitted to send for that address; the plugin checks the syntax only, so a refusal by your mail infrastructure (SPF/DMARC) shows up when the mail is sent, not when the mailbox is saved.
0.5.0 (2026-09-01)
Compatible with Redmine 7.0.x, 6.1.x, 6.0.x, 5.1.x.
Added¶
- The issue list can show a "Customer email" column. The existing Customer column renders the contact's display name, which for many senders is long or unclear — a "Firstname Lastname | Company | Department" display name tells an agent less at a glance than the plain address. The new optional column shows only the contact's email address and is sortable like its sibling. The existing Customer filter matches name or email, so it covers the new column too and no second filter is needed. The value is deliberately plain text, not a
mailto:link — links in dense lists get clicked by accident.
Fixed¶
- The Customer column and filter now also work for agent-created tickets with an assigned customer. Both used to read the sender of the first incoming mail only, but a ticket an agent creates and assigns a customer to (initial-mail flow) has no incoming mail until the customer replies — its customer was invisible in the list and unfindable through the filter, although the ticket sidebar showed it. Columns, sorting and filter now resolve the customer the way the rest of the plugin does: the authoritative ticket link first, the first incoming sender as fallback for legacy tickets.
- Deleting a contact no longer leaves broken ticket links behind. The ticket-to-customer link now clears when its contact is deleted (as it always did for the message log), and the customer columns and filter skip stale links from earlier deletions instead of showing a blank customer although a valid sender is on record.
Upgrade notes¶
No migration and no new permission or setting. One visible change: tickets whose customer was assigned by an agent now show that customer in the Customer column and are found by the Customer filter — before this release those cells were empty and the filter missed them.
0.4.0 (2026-08-13)
Compatible with Redmine 7.0.x, 6.1.x, 6.0.x, 5.1.x.
Added¶
- New tickets can be assigned to a fixed user or group, per project. Until now the plugin could only put a ticket on the agent who happened to answer it: a ticket arriving by mail was created unassigned, and the only way to route it anywhere was a mailbox rule matching subject or sender. Teams working out of a shared queue — 2nd level, an on-call group, a dispatcher — could not express that at all, because nothing in the plugin could address a group. Project settings → expert Helpdesk → Reply settings now carries Assign new tickets to, a dropdown of the project's assignable principals split into Users and Groups, preset to "none" so nothing changes for existing projects. It applies when incoming mail creates a ticket, and only then — replies never re-assign, so an agent's manual decision always stands.
- Mailbox rules can assign to a group. The rule action Assign to resolved its value against the project's members and could therefore only ever find a user, which left the rules engine unable to express the same routing the new project default supports. The dropdown is built from the project's assignable principals, groups marked as such, and stores the principal id. Rules created before this release store a login and keep resolving unchanged.
Changed¶
- "Assign ticket to me after reply" only takes effect while the ticket is still unassigned. The option overwrote the assignee on every single reply, so a ticket a dispatcher had just routed to 2nd level was silently taken over by the first agent who answered — and an assignee picked in the very same form was discarded on send. It now claims the ticket only when nobody holds it, which is what the option was meant to do; existing assignments, to a user or a group, are left alone.
- Rules assigning a ticket now use the project's assignable members rather than all members. A member whose role is not assignable could previously be set as assignee by a rule, which the ticket form itself would refuse.
Upgrade notes¶
This release runs a migration. One column is added to the per-project settings:
bundle exec rake redmine:plugins:migrate NAME=redmine_expert_helpdesk RAILS_ENV=production
No new permission is introduced, and no existing project changes behaviour on upgrade: Assign new tickets to starts empty, which means "do not assign" — exactly what happened before.
The reply option behaves differently after the upgrade. If you relied on Assign ticket to me after reply to move a ticket to whoever answered last, it no longer does that once somebody is assigned. Clear the assignee to hand a ticket back to the pool, or set a project default so tickets start on a group and agents claim them from there.
Three settings now decide who owns a new ticket, in this order: an Assigned to: keyword in the mail, which Redmine's own MailHandler has already honoured before the plugin looks; a matching mailbox rule; and the project default. The option list comes from the same source as the ticket form, so Groups appear only while Allow issue assignment to groups is enabled under Administration → Settings → Issue tracking. A user or group that later loses its role or leaves the project is silently skipped instead of producing invalid tickets — the setting is not cleared, so restoring the membership restores the routing.
0.3.0 (2026-08-06)
Compatible with Redmine 7.0.x, 6.1.x, 6.0.x, 5.1.x.
Added¶
- Prior ticket content can be quoted into the reply with one click. Agents answering a customer were copy-pasting out of the ticket history by hand, which loses the
>prefixing mail clients need to fold a quote — and silently drags along whatever happened to be selected. A Quote button now sits next to the formatting icons of the note editor with three entries: Original email quotes the ticket description (the mail that became the ticket, embedded images already resolved), Complete conversation adds every public journal note, and Email conversation adds only the notes belonging to a mail actually exchanged with the customer. Entries carry author and timestamp and are separated by a horizontal rule, so a long history stays readable while scrolling. Private notes are excluded from all three — including for agents who are allowed to read them, because the text is destined for a customer.
- Answer templates, globally and per project. Support cases repeat, and until now every acknowledgement, follow-up question and closing text was retyped. A Templates button next to the Quote button inserts a stored answer. Global templates are maintained under Administration → Plugins → Redmine expert Helpdesk, project templates in the project settings tab expert Helpdesk; a project's own templates are offered first, so it can override a central wording by reusing its name. Templates understand the same macros as autoresponder, header/footer and subject templates, and the macros are expanded at insertion time against the ticket, its customer and the acting agent.
- Embedded images of an incoming mail are shown in the ticket instead of the
[cid:…]marker the mail client left behind, so a signature arrives as a logo rather than as[cid:image001.png@01DD2980.37ED1560]. Outlook's[cid:…], Gmail's[image: …]and<img src="cid:…">are all recognised and rewritten to the image syntax of the configured formatting, pointing at the attachment that was just stored. The archived.emlkeeps the untouched original, and markers without a matching attachment are left alone.
- Outgoing mails carry their send time in the journal header, the same way received mails already did, so the correspondence can be followed on one time axis instead of reading the outgoing side off the journal's own timestamp — which is when the note was saved, not when the mail left.
- The mailbox connection test can copy the whole message. Provider errors are long and the status line wraps; Copy message puts the full text on the clipboard, and where the clipboard API is unavailable — an internal Redmine over plain http is not a secure context — it selects the text instead of failing silently.
Fixed¶
- Images already attached to the ticket never reached the customer. Only images an agent had just pasted or dropped into the form were sent as inline mail parts; anything already stored on the ticket stayed a relative reference that means nothing in a mail client, so the customer received an empty box. This was invisible while pasting was the only way to get an image into a reply, and became obvious once quoting made it easy to reference the pictures of the original mail.
-WhatIfdid not stop the Entra ID writes insetup-azure-app.ps1. It was documented as a dry run and guarded the Exchange Online calls, but the Microsoft Graph ones ran regardless.
Changed¶
setup-azure-app.ps1can add mailboxes to an existing setup instead of aborting as soon as an app registration with the given name exists. Onboarding one more project no longer means tearing the tenant setup down and rebuilding it — which would mint a new client secret and force a Redmine config change.
-ListRoleAssignmentsshows which mailboxes a scope actually covers, by asking Exchange Online to evaluate the filter rather than reading it by eye — the only way to answer that for aCustomAttribute, domain or group filter.
Upgrade notes¶
This release runs a migration. One table is added for the answer templates:
bundle exec rake redmine:plugins:migrate NAME=redmine_expert_helpdesk RAILS_ENV=production
No new permission is introduced. Quoting and inserting a template require the existing Send customer replies permission, the same one that governs the reply form; maintaining a project's templates requires Manage helpdesk mailboxes, and the global templates are administrators only.
Quoting deliberately never includes private notes, not even for agents holding View private notes — the result is meant for a customer. For the same reason the plugin's own bookkeeping notes (autoresponder sent, phishing links removed) stay out of the quoted conversation. Very long histories are capped so the note field stays usable, and the toolbar says so rather than truncating silently.
Embedded images can be switched off under Administration → Plugins → Redmine expert Helpdesk → Embedded images if the previous behaviour is preferred.
0.2.4 (2026-08-06)
Compatible with Redmine 7.0.x, 6.1.x, 6.0.x, 5.1.x.
Fixed¶
- The "AI statistics" tab no longer appears when the AI features are switched off. It showed up in every helpdesk project for anyone holding the global View AI statistics permission, even with both the AI features and the knowledge base disabled — where it led to a permanently empty page. The tab now appears once at least one of the two is enabled, because the page reports both AI summaries and knowledge-base requests, and either one alone makes it meaningful. The page itself answers 403 while both are off, so it can no longer be reached by typing the URL.
- Every checkbox in the plugin settings and the mailbox form shipped a duplicate DOM id. Each checkbox is preceded by a hidden field carrying its unchecked value, and both were given the same id, derived from the shared field name — so
getElementByIdreturned the invisible hidden field instead of the checkbox. Twelve checkboxes were affected. Saving the forms was never affected, which is why it went unnoticed.
Upgrade notes¶
No migration, no new setting, no new permission.
One visible change on upgrade: sites that hold the View AI statistics permission but run with the AI features and the knowledge base switched off will find the tab gone. Enabling either one under Administration → Plugins → Redmine expert Helpdesk brings it back with all of its history intact — nothing is deleted, only hidden.
0.2.3 (2026-08-06)
Compatible with Redmine 7.0.x, 6.1.x, 6.0.x, 5.1.x.
Added¶
- Tickets waiting for a response are now visible at a glance. When a customer replied by mail to an existing ticket — or a reply reopened a closed one — nothing marked that ticket as needing attention, and agents had to fall back on filtering by SLA status. A ticket is now flagged Awaiting response as soon as an inbound reply arrives on an existing ticket, and the flag clears the moment an agent posts a public note or closes the ticket. Four places show it: a sortable Awaiting response column and filter in the issue list, a marker on the row itself, a counter in the issue-list sidebar, and a "Helpdesk: awaiting response" block for My page. The stored timestamp is that of the oldest unanswered reply, so a second mail does not make a ticket that has been waiting for days look fresh. A private note deliberately does not clear the flag — an internal remark is not an answer to the customer.
- Short mails no longer cost an AI call. A two-line "please call me back" summarises to itself, yet every ingested mail was sent to the provider. The new Min. input length setting skips the call below a configurable number of characters.
- AI diagnostics have their own log level. The diagnostic lines about input length and skip decisions can be raised, lowered or silenced independently of the rest of the log. AI errors are always logged.
- Every outgoing mail is logged with the transport it took. Replies, initial mails, autoresponder mails and SLA notifications leave over three different transports and previously all looked alike in the log. Each send now records which one it used; failures are always logged as errors.
Fixed¶
- Auto-reopening a ticket now shows up in its history. The reopen status was written without a journal entry, so a ticket jumped from closed back to open with no trace in the ticket history or the activity feed — nothing indicated why it had reopened. The status change is now recorded on the journal that the inbound reply already creates: one history entry rather than two, and no extra notification mail.
- Graph was offered as the send path for IMAP mailboxes it cannot serve. The rule read the mailbox's
oauth_presetcolumn, which is not in force for a mailbox using the central credentials, so the option appeared where it could not work.
Changed¶
- The send path moved next to the mail provider in the mailbox form, where it belongs — it governs replies, initial mails and the autoresponder alike, not just replies.
Upgrade notes¶
This release runs a migration. Two columns are added to helpdesk_ticket_infos for the awaiting-response flag:
bundle exec rake redmine:plugins:migrate NAME=redmine_expert_helpdesk RAILS_ENV=production
Existing tickets start unflagged; the flag is set from the next inbound reply onwards.
The feature is on by default and can be switched off under Administration → Plugins → Redmine expert Helpdesk. Switching it off hides the column content, row markers, sidebar counter and My page block. No new permission is needed — the existing expert Helpdesk project module and the Send customer replies permission govern it, the latter deciding whose public note counts as an answer.
0.2.2 (2026-08-04)
Compatible with Redmine 7.0.x, 6.1.x, 6.0.x, 5.1.x.
Added¶
- Mailboxes can now be managed over the REST API. The generic IMAP/SMTP support in 0.2.0 gave a mailbox a large configuration surface — which backend it uses, IMAP and SMTP hosts, OAuth2 credentials, folders — but none of it could be reached from outside the web interface, and a ticket fetched over the API did not even say which backend its mail had arrived through.
There is now a full set of endpoints: list and create mailboxes per project, read, update and delete an individual one, and a test_connection call that probes the stored settings and reports the folders it can see. Reading requires the same manage_helpdesk permission as writing, because a mailbox exposes mail hosts, usernames and OAuth client ids.
Secrets go in but never come back out: the mailbox password, OAuth client secret and service-account key can be set through the API and are never returned. Responses only report whether one is stored. Sending a single hyphen clears a stored secret, matching the masked fields in the web interface. The interactive OAuth consent stays in the browser, where an identity provider needs it.
- AI summary and knowledge-base settings are available over the API. The per-project settings endpoint quietly left out every AI and knowledge-base field, so those two features could only be configured by hand. They are now read and written like the SLA and phishing settings.
Fixed¶
- Phishing detection stopped working on Ruby 4.0. Microsoft SafeLinks and other redirect links are unwrapped by reading the original address out of the link's query string. The method used for that was removed in Ruby 4.0, so on Redmine 7 every link carrying a query string raised an error: SafeLinks were no longer decoded and redirect links were never flagged. Query strings are now parsed with a method that is not going away.
- Documentation no longer describes the plugin as Microsoft-365-only. The README still opened by calling this a Microsoft 365 plugin, months after generic IMAP/SMTP had shipped, and the plugin description shown in Redmine's own plugin list said the same. Both now state the actual choice of backends. The READMEs also gained a table of contents, and the German edition was brought back in line with the English one — it was missing a section entirely.
Upgrade notes¶
No migrations and no configuration changes. Existing mailboxes are untouched.
To use the new endpoints, enable the REST web service under Administration → Settings → API; requests authenticate with the usual X-Redmine-API-Key header and are authorized by the existing helpdesk permissions.
0.2.1 (2026-08-04)
Compatible with Redmine 7.0.x, 6.1.x, 6.0.x, 5.1.x.
Fixed¶
- The folder fields on the mailbox form are real dropdowns now. All five (source, processed, skipped, failed, sent) were text inputs backed by an HTML
datalist, which browsers draw exactly like their own autofill history — so a folder genuinely read from the mailbox was indistinguishable from a value typed into the field once before.
Each field now has a chevron that opens the full folder list and filters by substring while typing, so arbeit finds Verarbeitet. Arrow keys, Enter, Escape, hover and mouse selection all work. Free text stays valid: a folder that does not exist yet is offered for creation and is still created when the form is saved.
The field and the list follow the active Redmine theme — the input uses the same chevron and metrics as a native select, and the list reuses Redmine's own autocomplete styling. No new dependency.
0.2.0 (2026-08-03)
Compatible with Redmine 7.0.x, 6.1.x, 6.0.x, 5.1.x.
Added¶
- Generic IMAP/SMTP mailboxes. Until now mail could only be fetched from Microsoft 365 via the Graph API. A mailbox now picks its backend itself, so Google Workspace, Exchange on-premises, self-hosted servers (Dovecot, Zimbra) and ordinary hosters work as well. Incoming mail arrives over IMAP, outgoing mail leaves through the mailbox's own SMTP server.
- OAuth2 (XOAUTH2) as the default authentication, with three grants:
client_credentials(application-only),authorization_code(one-time consent through the browser) andjwt_bearer(service account with domain-wide delegation, assertion signed locally). Username/password over TLS stays available for servers without OAuth2. - Per-mailbox secrets are stored encrypted — client secrets, refresh tokens, passwords and service-account keys. Existing plaintext values keep working, so no data migration is forced.
- Connection presets for Microsoft, Google and generic servers prefill host, port and encryption, and central defaults can be set once under Administration for operators running a single mail server.
- "Test connection" button on the mailbox form: verifies login, lists the folders and probes the outgoing route in one step.
Fixed¶
- Rotating an OAuth2 client secret did not invalidate the cached access token — the token issued for the superseded secret stayed in use until it expired.
- A new IMAP mailbox defaulted to a reply transport its own validation rejects.
- Connection presets and the central defaults could never set a port or an encryption mode; only the host fields worked.
- An IMAP mailbox could be configured to send through Microsoft Graph even when Microsoft does not host it.
- The autoresponder ignored the mailbox's reply transport and always sent through Graph.
- Boot failure and a silently missing OAuth controller when installed alongside RedmineUP's helpdesk plugin.
Upgrade notes¶
Migrations 034–037 run on upgrade. Existing Microsoft 365 mailboxes are untouched and keep using the Graph API — the new backend is opt-in per mailbox.
0.1.6 (2026-08-03)
Compatible with Redmine 7.0.x, 6.1.x, 6.0.x, 5.1.x.
Documentation: "Flow per mailbox fetch" brought back in sync with `MailProcessor`.
The diagram had drifted and was missing the auto-reply filter, the phishing scan (quarantine vs. neutralize), the MIME preprocessing stage (thread-header stripping for the reopen-age limit, `Auto-Submitted` stripping on NDRs, `In-Reply-To`/`References` injection), ticket reopening on replies, the `HelpdeskTicketInfo` link and the async AI-summary enqueue.
It also showed a single "target folder" where the code actually uses three (`processed_folder` / `skipped_folder` / `failed_folder`, with fallbacks).
Adds notes on target folders and per-message failure isolation. Mirrored in `README.de.md`.
`LICENSE` file (GPL-2.0-or-later)
The plugin is now explicitly licensed under the GNU General Public License v2 or later, matching Redmine itself.
Adds a copyright/license header to `init.rb` and a License plus Third-party components section to `README.md` / `README.de.md`, documenting the bundled MIT-licensed Chart.js 4.4.6 and chartjs-plugin-datalabels 2.2.0, both served locally — no CDN request at runtime.
Screenshots in both READMEs
`README.md` and `README.de.md` now open with a short gallery of the main screens (SLA statistics, AI statistics, customer list, ticket view) and carry inline screenshots in the customer, AI-summary and knowledge-base sections, so the plugin can be evaluated without installing it first.
Images live in `docs/screenshots/{en,de}/` and are excluded from the release archives (`--exclude='docs'` in `.github/workflows/release.yml`), so the installable package does not grow.
New maintenance scripts
`scripts/seed_screenshot_demo.rb` and `scripts/teardown_screenshot_demo.rb` build and remove the synthetic demo project the screenshots are taken from (contacts, tickets with a realistic SLA mix, AI request log, knowledge-base entries).
Both are scoped to a single project, write no global settings and are excluded from the release archives along with the rest of `scripts/`.
0.1.5 (2026-07-29)
Compatible with Redmine 7.0.x, 6.1.x, 6.0.x, 5.1.x.