Service Desk
Email to ticket in WordPress, without the usual email failures
Anyone who has run support email through a WordPress plugin has lived through both of these.
The first: a customer replies to your answer, their mail client rewrites the subject line — or they retype it — and their reply lands as a second, unconnected ticket. Two agents pick the two tickets up and answer separately, contradict each other, and the customer forwards both replies back with a question about which one is correct.
The second: two cron runs overlap, or a webhook provider retries a delivery it thinks failed, and one email becomes three tickets. Somebody spends Monday morning merging them.
Neither of those is bad luck. Both are architectural: the first happens when the subject line is used as the primary threading signal, and the second happens when duplicate protection lives in application code instead of in the schema. This page explains exactly how Stillpoint's email piping avoids each one, in enough detail that you can check the claims.
Email to ticket is part of the Service Desk, included in Pro ($299/yr, up to 5 sites) and Agency ($499/yr, up to 20 sites), with unlimited agents.
Multiple mailboxes, each with its own identity
Support rarely arrives at one address. Returns go to returns@, billing questions go to accounts@, resellers write to wholesale@, and a handful of named accounts have been given a vip@ address that somebody promised would be answered faster. In most WordPress ticket systems that reality is flattened into one inbox and then patched up with rules, which is why replies start going out from a generic system address nobody recognises.
In Stillpoint each mailbox is a first-class object. It carries its own team, its own brand, its own SLA policy, its own signature, its own auto-reply text, its own ticket-number prefix and its own default priority. A ticket that arrives at returns@ is numbered RET-1042, sits in the returns queue, is measured against the returns SLA and is answered with the returns signature.
The part that matters to the customer is simpler: a reply always leaves from the address they wrote to. If they emailed accounts@, the answer comes from accounts@, and their own reply-to-that-thread behaviour keeps working the way they expect.
The specifics
- One install, many mailboxes: support@, returns@, accounts@, wholesale@, vip@
- Per mailbox: team, brand, SLA policy, signature, auto-reply, default priority
- Per-mailbox ticket number prefixes (SUP-, RET-, ACC-) so a reference tells you the queue
- Outbound replies always use the mailbox the customer wrote to, never a generic system sender
- Each mailbox has its own health state, credentials and sending configuration
Five ways to connect a mailbox
Different organisations have different constraints. A small store with a cPanel mailbox wants to be collecting mail in five minutes. A 900-seat Microsoft estate will not hand out an app password, and needs OAuth signed off by a tenant administrator. Somebody else does not want the helpdesk touching the mailbox at all and would rather forward a copy. All of those are supported, and none of them is a second-class path.
IMAP is the fastest route and needs no registration anywhere. It is worth being explicit about one detail: TLS certificate verification is always on and there is no setting to switch it off. A helpdesk that offers a checkbox to accept an invalid certificate is offering to accept anyone's mail, and that checkbox is usually ticked during a hurried setup and never unticked.
OAuth is worth the paperwork for large Google or Microsoft estates, where central credential policy makes app passwords a dead end. In both cases the application is registered in your own tenant, so the trust relationship is between your mailbox and your own site — not between your mailbox and us.
| Method | Setup effort | What you need | Best for |
|---|---|---|---|
| IMAP | Lowest — works immediately | Host, port, username, app password. TLS verification always on, with no option to disable it. | Anyone who wants mail flowing today, including cPanel, Zoho, Fastmail and Google or Microsoft mailboxes with an app password |
| Gmail / Google Workspace (OAuth) | Medium — one-time tenant setup | An OAuth application in your own Google tenant. Stillpoint stores a refresh token and never asks for your Google password. | Google estates where app passwords are blocked by policy |
| Microsoft 365 (Microsoft Graph, OAuth) | Medium — one-time tenant setup | An app registration in your own Microsoft tenant, authorised for the mailbox. | Microsoft 365 organisations with central credential control |
| Email forwarding | Low | A forwarding rule to an opaque generated address such as in+<32-hex-token>@your-inbound-domain — not guessable. | Teams who will not grant mailbox access, or who want a copy rather than collection |
| Provider inbound webhooks | Medium | SendGrid, Mailgun or Postmark configured to post the raw message. A generic raw field and JSON payloads are handled too. | High volume, or where near-instant delivery matters more than polling |
The specifics
- IMAP needs no registration with anyone and is the fastest way to start
- TLS certificate verification on IMAP is permanent — there is no insecure mode to enable by accident
- Gmail and Microsoft 365 OAuth apps live in your own tenant; only a refresh token is stored
- Forwarding targets an opaque 32-hex-character inbound address rather than a predictable one
- Inbound webhooks cover SendGrid, Mailgun and Postmark, plus generic raw and JSON payloads
- Mailbox credentials are encrypted at rest and never shown back in the interface
Threading — the part most plugins get wrong
A customer replies to your answer, their mail client rewrites the subject line, and their reply arrives as a brand new ticket. Two agents now answer the same person separately, and the customer concludes you are not paying attention. This is the single most common complaint about email piping plugins, and it happens because the subject line is being used as the primary thread signal when it is the weakest one available.
Stillpoint matches inbound mail through an ordered cascade, strongest signal first. Each step is something we either control cryptographically or can verify against messages we have actually sent.
Step five is where other systems open a security hole. If a ticket number in the subject is on its own enough to attach a message to a conversation, then anybody who learns a ticket number — a forwarded email, a screenshot, a guessed sequence — can inject a message into that conversation and, depending on the system, read the reply. In Stillpoint a matching ticket number from an unrecognised sender opens their own ticket instead. Nothing is lost, and nothing leaks.
Every message records how it was matched. When a thread does go somewhere unexpected, you can open the message and see which signal was used, which makes a mis-thread a diagnosable event rather than a mystery you argue about.
The matching cascade, in order
- 1Signed reply token — Carried in the reply address and in a header we control. HMAC-signed, so it cannot be forged, and knowing a ticket number tells an attacker nothing useful.
- 2RFC In-Reply-To — Matched against the Message-IDs of messages we have actually sent or seen on that ticket.
- 3The References chain — Any id in the chain, newest first — which is what rescues replies from clients that mangle In-Reply-To.
- 4The provider thread id — Where the connector supplies one, such as Gmail or Microsoft Graph conversation identifiers.
- 5A ticket number in the subject — Used only if the sender is already a known participant on that ticket. A stranger quoting the number does not get in.
- 6Otherwise — A new ticket, with the reason it was treated as new recorded on it.
The specifics
- Subject-line matching is the last resort, not the first check
- The reply token is HMAC-signed, so ticket numbers are not secrets that need protecting
- A ticket number quoted by an unknown sender creates their own ticket rather than joining an existing thread
- Subject changes, forwarded replies and mangled In-Reply-To headers are all recoverable via the References chain
- The match method is stored on every message, so threading behaviour is auditable
Duplicates are impossible by construction
The second classic failure is the same email becoming two or three tickets. It happens when two cron runs overlap, when a webhook provider retries a delivery it believes failed, or when an IMAP session is interrupted after reading but before marking. Any of those can be papered over with careful code — until somebody edits that code.
Stillpoint does not rely on careful code here. Deduplication is a UNIQUE constraint in the database on the mailbox plus a delivery key: the provider message id where one exists, else the RFC Message-ID, else the IMAP UIDVALIDITY:UID pair, else a content hash. The record is written before any parsing work happens, so a duplicate delivery is rejected at the cheapest possible moment rather than after a megabyte of MIME has been decoded.
Stated plainly: this is a schema guarantee, not a code path. A future logic bug cannot reintroduce duplicate tickets, because the database will refuse the insert.
The specifics
- UNIQUE constraint on (mailbox, delivery key) enforced by the database itself
- Delivery key precedence: provider message id → RFC Message-ID → IMAP UIDVALIDITY:UID → content hash
- The dedupe row is written before parsing, so duplicates cost almost nothing to reject
- Overlapping cron runs and webhook retries are therefore safe by design
- The collection worker is safe to run every minute and safe to run twice concurrently
What arrives in a real inbox, and what we do with it
A support mailbox does not receive a tidy stream of customer questions. It receives out-of-office replies, bounce notifications, mailing-list traffic, spam, messages far larger than your upload limit, and the occasional message whose MIME structure is simply broken. How a helpdesk behaves on those is the difference between a queue you can trust and a queue somebody has to clean out every morning.
Nothing in this table is silently deleted. Every non-standard outcome leaves a record with a stated reason, and anything quarantined can be inspected and released.
| Message type | How Stillpoint handles it |
|---|---|
| Normal customer reply | Appended to the ticket as a public message. Quoted history is trimmed for readability and the original is always kept. |
| Out-of-office / vacation reply | Filed as an internal note. It does not reopen the ticket and never triggers our auto-reply. Detected via RFC 3834 Auto-Submitted, Microsoft's X-Auto-Response-Suppress and the legacy Precedence: bulk convention — all three, because in the wild systems set only one. |
| Bounce / delivery failure | Recorded against the reply that failed and shown on the ticket, so an agent never assumes a message arrived. Hard bounces also feed the marketing suppression list. |
| Mail loop | Suppressed by four independent brakes: header detection, our own loop stamp coming back to us, a per-sender rate guard, and a hop-count check. |
| Spam | Filed in a Spam status rather than deleted, so a false positive is recoverable by a human. |
| Oversize message | Quarantined with the reason stated — size, or attachment limit — and never silently dropped. |
| Malformed MIME | Falls back to a text representation rather than discarding the customer's message. A genuinely unparseable message is quarantined, not allowed to crash the worker. |
The specifics
- Auto-reply detection checks three independent conventions, not just RFC 3834
- An automated reply becomes an internal note, so it neither reopens the ticket nor restarts a response clock
- Bounces are visible where the agent is working, not buried in a log
- Spam, oversize and unparseable mail are retained in a recoverable state with a stated reason
- Four independent loop brakes, so no single failure produces an overnight mail storm
Attachments and inline images
Customers explain problems with screenshots, and screenshots pasted out of Outlook arrive as inline CID references rather than ordinary attachments. If a helpdesk does not rewrite those references, the agent sees a broken image icon and asks the customer to send it again. Stillpoint rewrites inline images to authorised URLs so they render in the conversation the way the sender intended.
Attachment handling is deliberately suspicious. Files are checked by magic bytes as well as by file extension, so a file called invoice.pdf that begins with a Windows executable header is rejected regardless of its name. And attachments are only ever served as downloads, with nosniff, never rendered inline — which is what stops a stored SVG or HTML attachment from executing script in your site's origin.
The specifics
- Inline (CID) images rewritten to authorised URLs, so pasted screenshots display
- Content sniffed by magic bytes as well as extension; mismatches are rejected
- An executable disguised as invoice.pdf does not get stored
- All attachments served as downloads with X-Content-Type-Options: nosniff, never inline
- SVG and HTML attachments therefore cannot execute in your site's origin
Privacy: remote images are blocked by default
A remote image in an email is a tracking device. When your agent opens a message and the client loads that image, the sender learns the exact moment it was read and the IP address it was read from. For a support team that is a genuine problem: it tells anyone who emails you when your staff are working, how quickly they triage, and roughly where they are.
Stillpoint neutralises external image sources by default and shows a labelled placeholder instead. An agent who needs to see the image can choose to load it, which is a deliberate decision rather than an accident of opening a ticket.
The specifics
- External image sources neutralised on arrival, by default
- A labelled placeholder is shown, so the agent knows content was withheld rather than missing
- Loading remote content is an explicit per-message action
- Inbound HTML is sanitised on arrival against an allow-list
When it goes wrong, you can find out why
Anyone who has run a helpdesk knows the real question is not whether email collection will break, but how long it takes to discover that it has. A red dot that says "error" is not an answer. This is the part of the system built for the person who gets asked why a customer's email never turned into a ticket.
Each mailbox has a health panel: connected state, last successful sync, current lag, messages processed today, problems today, queue backlog, send failures in the last 24 hours, authentication state and consecutive failure count. Every non-healthy state carries a stated reason — authentication rejected, connection timed out, certificate mismatch — rather than a colour.
Alongside it there is a searchable mail log, filterable by mailbox, state, classification, ticket and Message-ID. It deliberately does not contain message bodies: an administrator browsing a diagnostic log should not be reading customers' mail. And there is a failed-mail queue you can inspect and retry. Nothing is ever silently discarded; a message that exhausts its retries is marked and kept rather than deleted. Service managers are notified after three consecutive mailbox failures, so an expired password is a notification rather than a discovery.
The specifics
- Per-mailbox health: connected state, last sync, lag, processed today, problems today, backlog
- Send failures in 24 hours, authentication state and consecutive failure count
- Every unhealthy state states a reason instead of showing a red dot
- Searchable mail log filterable by mailbox, state, classification, ticket and Message-ID
- The mail log excludes message bodies by design
- Failed-mail queue is inspectable and retryable; exhausted messages are kept, not deleted
- Service managers notified after three consecutive mailbox failures
Sending, and honest delivery status
Replies go out through the mailbox's own SMTP configuration, which is also how you use a delivery provider: point the mailbox at SendGrid, Mailgun, Postmark or Amazon SES as an SMTP relay and your From address stays yours. There is no separate sending identity to explain to customers.
Then the honesty point. When a provider accepts a message, Stillpoint reports "Accepted by <provider>. Delivery to the recipient is not confirmed." It does not print "Delivered", because that is not what the provider told us — acceptance by a relay is not arrival in a mailbox, and a helpdesk that conflates the two teaches agents to trust a status that does not mean what it says.
Temporary failures retry with exponential backoff. Permanent failures — a bad address, an authentication rejection — stop immediately rather than burning your sending reputation on a message that will never arrive, and the agent is told the customer did not receive the reply so somebody can pick up the phone.
The specifics
- Outbound via each mailbox's own SMTP, including provider relays (SendGrid, Mailgun, Postmark, Amazon SES)
- Your From address stays your address
- Status reads "Accepted by <provider>. Delivery to the recipient is not confirmed." — never a fabricated "Delivered"
- Temporary failures retry with exponential backoff
- Permanent failures stop immediately and are surfaced to the agent on the ticket
- Bounces are attached to the specific reply that failed
Scheduling: use a real cron
WordPress cron only fires when somebody visits the site. For a scheduled report that is tolerable; for collecting support mail it is not, because a quiet Sunday night means nothing is collected until the first visitor arrives on Monday.
Stillpoint therefore exposes a token-authenticated collection endpoint you can point a real system cron at. One line in crontab, and collection runs on a schedule that does not depend on traffic. There is also a "Run collection now" button for when somebody is standing over your shoulder asking where their email went.
One line of crontab
* * * * * curl -fsS "https://yoursite.com/stillpoint-crm/?page=svc_cron&t=YOUR_TOKEN" >/dev/nullThe specifics
- Token-authenticated endpoint for a real system cron, independent of site traffic
- Safe to run every minute
- Safe to run twice concurrently — overlapping runs cannot duplicate tickets
- "Run collection now" available in the interface for manual checks
- Collection lag is visible per mailbox, so a stopped cron is obvious
Email to ticket questions
Yes, and that is the intended setup. Stillpoint connects to the mailbox you already have — over IMAP, Gmail or Microsoft 365 OAuth, or by forwarding — and replies leave from that same address. Customers never see a different sender, and you do not have to announce a new support email.
No. IMAP collection and email forwarding both leave your existing mail flow completely untouched: mail still arrives where it arrives today. MX changes are only relevant if you decide to route a dedicated inbound domain at a provider webhook, and that is optional.
Yes, over OAuth. You register an OAuth application in your own Google tenant and authorise the mailbox; Stillpoint stores a refresh token and never asks for your Google password. If you would rather not register an app, a Google app password over IMAP works immediately.
Yes, via Microsoft Graph over OAuth, with an application registered in your own Microsoft tenant. As with Google, IMAP remains available if you want to start collecting mail before any tenant admin work happens.
Nothing is lost. Messages stay in the mailbox and the next successful poll collects them. The mailbox health panel records the failed attempts and the reason, and service managers are notified after three consecutive failures so an outage is noticed rather than discovered a day later.
Not when the reply carries any of the signals we control or the RFC headers, which covers essentially every real mail client — including replies that change the subject line, because the subject is the last signal we consult rather than the first. If a genuinely new message arrives with no recoverable thread, it opens a new ticket, and the record shows how it was matched.
Collision detection shows that a colleague is already replying to the ticket before you start typing, and a stale save is refused rather than silently overwriting their work. That is a Service Desk workspace behaviour rather than an email one, but it is the other half of the same problem.
Only if you switch raw retention on, and then only for the retention window you set. By default Stillpoint keeps the parsed message and the metadata needed for threading and diagnostics, and the mail log deliberately excludes message bodies so an administrator browsing it is not reading customers' mail.
Where to next
- SLA management — business-hours targets, pausing, escalation ladders and defensible reporting.
- Service Desk overview — Customer 360 on every ticket, routing, portal, knowledge base and incidents.
- Stillpoint vs SupportCandy — how the two compare for WordPress support teams.
- Features — every module documented, from pipelines to work management.
- Pricing — Pro and Agency, both with unlimited agents.
Turn your support mailbox into a queue you can trust
Email to ticket is part of the Service Desk, included in Pro ($299/yr) and Agency ($499/yr) with unlimited agents. Every message stays on your own server.
Unlimited users · Flat price per site · 30-day money-back guarantee