Omniscio documentation
Browse all documentation
  1. Getting Started13
  2. Sessions & Agents115
  3. Inbox & Notifications59
  4. Projects & Tasks95
  5. Automation & Scheduling75
  6. Knowledge & Memory26
  7. AI Features60
  8. Integrations100
  9. Plugins & Marketplace33
  10. Cloud & Teams56
  11. Settings & Customization58
  12. Account & Billing28
  13. Troubleshooting84
  14. CLI & API Reference22
  15. Legal & Policies4
  16. Uncategorised22

Help Desk email tickets — a plain-English support email becomes a tracked ticket

Someone emails your support address in plain words and it becomes a real Help Desk ticket: it appears in the operator's incoming queue, advances a lifecycle stage on its own, and the reply goes back out as the support address on the reporter's own thread.

What it is

One-line: A person emails your support address in plain English — no [BUG: <slug>] subject marker — and it becomes a real Help Desk ticket: it appears in the operator "Incoming" queue with the sender's name on it, carries a lifecycle stage that advances by itself when an investigation starts, and their replies land back on the same ticket. They get one "we got it" straight away; Claude drafts the answer; a human presses send, and that send goes out as the support address on the reporter's own thread.

The gap it closes

Before this, email only became a ticket if the subject literally started with [BUG: <slug>] or [FR: <slug>] — the Bug Report Intake router. Real people do not write that. Their mail matched nothing, fell through to a generic "catchall" session that answered it once, and was never tracked. help_requests held zero rows on an install with months of real support mail.

Opening the door to the public

A ticket could only ever be started by someone on your approved-senders list, and that list is one setting for the whole install — so opening it to the public would have opened your personal agent address to the world with it. Two things fix that:

  • A public support address you claim from the Agent Email panel (the same panel that holds your personal address). It is a second address on your account, and the panel shows it, lets you copy it, and turns it off again with one click. Turning it off is a true rollback: with no support address set, mail is handled exactly as it was before.
  • A per-address sender rule. Mail arriving at the support address is judged by the support policy (anyone may write in); every other address on the account keeps exactly the policy it had. Naming a support address never widens anything else — and naming your own address as the support address opens nothing at all.
  • Only support mail becomes a ticket. Once a support address is set, a new email to your personal agent address is not turned into a ticket — it goes to your agent as a normal conversation, the way emailing your agent always worked. A customer's reply on a ticket still joins that ticket, whichever of your addresses it arrives at.

A reserved name like support@ is refused for everyone except an owner/admin, and that refusal is enforced by the server, not just by this app — otherwise any signed-in user could claim the name by calling the cloud directly.

A per-sender daily limit (10 new tickets from one address) still bounds what one stranger can trigger, because each ticket can start a paid investigation session.

In-development, OFF by default

Reveal via Settings → Features → Helpdesk email ticket intake (helpdeskEmailTicketIntakeEnabled) or AMC_SHOW_HELPDESK_EMAIL_TICKET_INTAKE=1. It has its own switch — the helpdeskEnabled toggle above it gates the in-app "Get Help" ask panel, a different consumer this leg does not touch.

With the switch off, the gate returns before any database read and inbound mail behaves exactly as it did before the feature existed.

What happens to an incoming email

If you have turned on the spam filter, a stranger's new email is checked for junk before any of the steps below. Junk, and newsletters or auto-replies sent to the support address, wait in the Inbox's Spam list instead of becoming a ticket, and Not spam turns one into a ticket. A reply on a ticket you already have is never checked.

  1. Is it a person? Judged on transport headers only, never on content — a real person asking an unusual question must never be mistaken for a mailer. Four signals: List-Unsubscribe, List-Unsubscribe-Post (RFC 8058), Precedence: bulk|list|junk, Auto-Submitted, plus a no-reply-style sender address.
  2. What kind of report is it? bug / feature / feedback / question, classified by the same prescreen LLM call that already runs on every inbound message — no second call, so the cost is flat and the existing daily cap and circuit breaker still govern.
  3. Become a ticket. A help_requests row plus a cloud thread, so it shows in the operator queue, stamped with a synthetic reporter built from the sender address.
  4. Bind both ways. The inbound tracking row carries the ticket id, so a later reply on that email thread appends to the same ticket (and reopens it if it was resolved) instead of opening a second one — and puts the ticket back under Pending with a fresh alert, even after you answered it. Deleting the ticket from your Get Help history only hides it there: your replies still reach the customer and their answers still join the same ticket.
  5. Investigate. A session spawns with the report as untrusted, fenced input — the same prompt builder the marked-subject router uses, so the operator's global and per-project appended instructions apply here too, with no extra setting. The model is the account default unless a tier is chosen (below).

The two knobs

Knob Where Default
Investigation model tier (Genius / Smart / Worker / Basic) Bug Intake settings unset — the spawn inherits the account default, exactly as before
The investigation prompt the existing global + per-project appended instructions boxes empty — the prompt is byte-identical to the marked-subject one

A tier, never a model id: the id is resolved at spawn time so the choice cannot go stale when a new model version ships.

The acknowledgement

A stranger who writes in hears nothing until an operator replies by hand — the claim that keeps the catchall away is the same thing that stops an automatic reply covering the gap. So a new ticket sends its reporter one short "we got it".

It fires at most once, and only for a brand-new ticket: the marker is written on the message's own row inside the ticket's transaction, and the send happens only for the caller that wins that write. A redelivered message, an overlapping poll, and a reply that appends to an existing ticket can none of them produce a second one. It records before it sends, so a send that fails is never retried — a duplicate "we got your message" to a stranger is worse than a missing one, and a missing one is exactly the behaviour before this existed.

Replying

Claude drafts into the existing console composer; a human presses send — on any operator's computer. The reply lands in the cloud thread, and the computer that received the customer's email mails it to them on their original message (In-Reply-To the thread root, so it lands in their existing conversation). Only that computer can: the link to the email, its subject and the support sender live on it alone. A reply written there goes out within seconds; one written on a teammate's computer goes out as soon as the receiving computer hears of it — and while that computer is off, the reply waits for it.

Every reply is emailed exactly once. Each reply carries its own "don't send twice" key, so the second and later replies on a ticket go out as reliably as the first, and a reply that arrives twice is never mailed twice. A reply only counts as handled once its email has gone (or the recipient's mail server refused it for good). If the send fails — a network hiccup, or no agent address set up yet — it is tried again automatically after 1, 5, 15, 30 and 60 minutes, and after that the next time the app starts. Three failures in a row raise the "can't send your agent's email replies" card in your inbox. Because the customer is the one asking, replies on an email ticket never tell the operator "an agent replied to your question".

In the console, an email ticket shows its sender, an Email tag and the email's own subject at the top, the reply box reads "Send your message to … by email", and the earlier conversation the customer's mail client quoted folds behind Show quoted text — see Get Help part 2.

The reply answers as the address the mail arrived at. A ticket that came in at the support address is answered from support, with Reply-To support, so the reporter's answer comes back to support rather than into your personal agent inbox — otherwise the promise breaks on the second message. A ticket with no recorded recipient (any that predates this) falls back to your single agent address, exactly as before.

Nothing sends by itself

This is structural, not a setting. Claiming the message stops the catchall session from spawning at all, and the inbound tracking row is written already-closed under a sentinel session id, so none of the six "needs a reply" selectors can find it. There is nothing for an automatic replier to act on.

The acknowledgement does not weaken this: it is a one-shot message of its own, sent on a path that never touches the reply marker or a session id, so it cannot re-arm anything an automatic replier looks for.

Their own board

Support-email tickets no longer share a lane with bug reports. The Bug Intake panel carries a Help Desk tab listing them alone — same rows, same stage control, same filters as the general Tracker board behind it, scoped to one origin. The distinction is recorded as a fact (the ticket came from the help-desk email leg) rather than guessed from the source or from the classifier's report type, so the board never shows unrelated reports.

Spend bounds

Its own limit before any paid spawn — unmarked mail exits the marked-subject router before that router's own caps are consulted, so this leg needs its own. The limit is per sender, never per inbox: every different customer always gets their "we got it" acknowledgement and their investigation, however busy the inbox is. What is limited is ONE address opening new ticket after new ticket — 10 a day — which is what a mail loop, a runaway script or an abuser looks like, and almost never a real person. There is no burst throttle: several support emails arriving together (a queue drained after a restart) each get their acknowledgement and their investigation.

Past that sender's limit, the email still becomes a ticket in the Incoming queue — only the "we got it" acknowledgement and the investigation are skipped, so a person answers it by hand. It never falls back to an ordinary email session, which would reply to the sender automatically. The limit counts only that sender's tickets that got the automated treatment, so a follow-up on an existing ticket never uses it up.

There is no limit on the whole inbox. Instead, when Help Desk support email reaches 100 in one day, you get one heads-up in your inbox ("Help Desk is getting a lot of email today") with the day's count. Nothing is blocked or slowed because of it — every email is still handled as usual — and a heads-up you dismiss does not come back that day. You can switch this alert type off like any other alert.

If a ticket gets stuck

Escalation can fail after the local ticket exists, which would leave it invisible in the operator queue. A repair pass rides the existing inbound poll and re-escalates such tickets, carrying the original reporter so a recovered ticket is not anonymous. It ignores tickets younger than 5 minutes (they may still be mid-flight) and gives up after 24 hours rather than retrying a broken one forever. A stuck ticket you deleted from your Get Help history is still repaired — the delete only hides it — and it is escalated exactly once.

Where to find it

This is not a screen you open — it is a path into one. The person emails your support address; the ticket lands in the Help Desk operator's Incoming queue; the answer is written and sent from there.

How it behaves

Deploy dependency

Three of the four bulk-mail signals depend on the Cloudflare email worker forwarding List-Unsubscribe, List-Unsubscribe-Post, Precedence and Auto-Submitted. Until that worker is deployed, only the no-reply sender check can fire. Every hop treats the headers as optional, so the desktop, the inboundEmail function and the worker can be deployed in any order without rejecting real mail.

Deploy note for the public intake

The reserved-name rule and the two-address lookup live in the agentEmailRelay Cloud Function, so opening a support address on a real install needs that function deployed to the shared shares project. The desktop half degrades safely without it: the address still works, but a reserved name is refused only in-app until the function ships.

For agents

Where it lives

  • Contract: helpdesk-email-ticket-intake-contract.md
  • Public intake contract: helpdesk-public-support-intake-contract.md
  • The claim + repair pass: src/main/services/helpdesk/helpdesk-email-intake.ts
  • The one-shot acknowledgement: src/main/services/helpdesk/helpdesk-acknowledgement.ts
  • The outbound reply: src/main/services/helpdesk/helpdesk-email-reply.ts
  • Who sends it (the reply receive leg — email first, record second): src/main/services/helpdesk/helpdesk-boot.ts
  • The quoted-text fold: src/renderer/src/features/helpdesk/helpdesk-quoted-reply.ts
  • The reply From / Reply-To rule: src/main/services/email/agent-email-reply-identity.ts
  • The per-recipient sender rule: src/main/services/email/agent-email-sender-policy.ts
  • The model tier stamp: src/main/services/helpdesk/helpdesk-investigation-model.ts
  • The bulk filter: src/main/services/email/machine-mail-filter.ts
  • The board: src/renderer/src/features/bug-intake/BugIntakeTrackerTab.tsx (the Help Desk tab is that board, filtered)

Related

Last verified 2026-09-28