Creating issues by email

Turn inbound email into issues, forwarded from Gmail, Outlook or any provider.

Settings → Email intake

Turns inbound support email into issues. One URL per org, forwarded to from Gmail, Outlook, or any provider — priority is auto-classified from the subject/body, and routing decides which project.

Why one URL?

The URL is a secret webhook endpoint that identifies your org — not a mailbox. It never decides where an issue goes or how urgent it is; that's decided fresh on every message by Routing rules and Priority classification on this page. Forward every mailbox to the same URL and add more rules underneath it instead of asking for a second URL.

Known limitation: routing matches the sender's address/domain, not which mailbox/alias received the mail. Forwarding several aliases to this one URL, you can't distinguish them by alias — only by who sent the message.

Payload shape

from and subject are required; body, messageId, and attachments are all optional.

{
  "from": "jane@customerco.com",
  "subject": "Total outage on prod",
  "body": "Everything is down since 10am",
  "messageId": "optional-unique-id-for-dedup",
  "attachments": [
    { "fileName": "screenshot.png", "mimeType": "image/png", "content": "<base64-encoded-bytes>" }
  ]
}
  • attachments — base64-encoded file bytes, not a raw multipart upload (a webhook can't receive one the way a browser form can). A data:...;base64, prefix is stripped if present.
  • Up to 10 attachments are stored per message — extras beyond that are silently dropped, not rejected; the issue is created either way.
  • Each attachment is capped at 10 MB decoded — an oversized or malformed one is skipped, logged, and never fails the issue.
  • Attachments land on the created issue exactly like any other upload — visible in its Attachments list.

Provider setup

Gmail has no native "forward as webhook" feature — use a small Apps Script that polls the inbox on a timer:

  • script.google.com → New project, paste the script below.
  • Run it once manually to authorize Gmail + URL-fetch access.
  • Add a time-driven trigger (Triggers → Add trigger), every 5–10 minutes.
function forwardNewEmailsToIntake() {
  var INTAKE_URL = '<your-intake-url>';
  var LABEL_NAME = 'IntakeForwarded';
  var label = GmailApp.getUserLabelByName(LABEL_NAME) || GmailApp.createLabel(LABEL_NAME);

  var threads = GmailApp.search('in:inbox -label:' + LABEL_NAME, 0, 20);
  threads.forEach(function (thread) {
    thread.getMessages().forEach(function (message) {
      var fromHeader = message.getFrom(); // e.g. "Jane Doe <jane@customerco.com>"
      var match = fromHeader.match(/<(.+)>/);
      var attachments = message.getAttachments().slice(0, 10).map(function (blob) {
        return {
          fileName: blob.getName(),
          mimeType: blob.getContentType(),
          content: Utilities.base64Encode(blob.getBytes()),
        };
      });
      var payload = {
        from: match ? match[1] : fromHeader,
        subject: message.getSubject(),
        body: message.getPlainBody(),
        messageId: message.getId(),
        attachments: attachments,
      };
      UrlFetchApp.fetch(INTAKE_URL, {
        method: 'post',
        contentType: 'application/json',
        payload: JSON.stringify(payload),
        muteHttpExceptions: true,
      });
    });
    thread.addLabel(label);
  });
}

Troubleshooting

  • Nothing showing up — confirm the forwarding tool ran (check its own execution history) and the URL matches exactly what's shown above.
  • Wrong project / priority — check Recent activity: a ROUTED entry into the default project with no matched keyword means no Routing/Priority rule matched.
  • Attachment missing — check the backend logs for a per-attachment warning (wrong base64, empty content, or over 10MB); the issue itself still gets created.
  • 404 from the URL — the token was regenerated (or is wrong) — copy the current URL again.