# AI Email Categorizer

Classify one test email at a time, apply a Gmail label and log the result. No replies, forwarding or deletion.

By AutomateHQ, an AI and workflow automation business serving companies in Singapore and Thailand.

Guide: https://automatehq.org/resources/n8n-ai-email-categorizer/

## Status and permission to use

Free to use and adapt in personal or commercial workflows. Provided as a starter without warranty. No signup. No secrets or pinned customer data. Local fixture tests cover Code node validation and export structure. Not yet imported into a live n8n instance or verified with provider credentials. Do not treat these checks as end-to-end certification.

## Requirements

- An n8n instance with Code, HTTP Request and Webhook nodes. The downloads use built-in nodes only; import compatibility still needs checking on your installed version.
- Your own OpenAI API account with billing enabled. Store the key in an n8n Header Auth credential named OpenAI: header Authorization, value Bearer followed by your key.
- A Google Sheets OAuth2 credential in n8n and a dedicated test spreadsheet. Enable the Sheets API and grant the connected account access to that spreadsheet.
- A Gmail OAuth2 credential in n8n with gmail.modify access and the Gmail API enabled. Use a test mailbox, not your primary inbox.
- Five Gmail labels: AutomateHQ-Test, AHQ-Sales, AHQ-Support, AHQ-Billing and AHQ-Other. Obtain category label IDs using Gmail users.labels.list or n8n’s Gmail label listing operation.

## Setup

1. Import the workflow JSON and create a sheet tab named EmailLog with the headers below.
2. Create the five labels in Gmail. Configure the four actual category label IDs in Configure workflow.
3. Connect the Gmail, OpenAI and Sheets credentials. Add AutomateHQ-Test to one synthetic plain-text test email.
4. Choose Execute workflow. The manual trigger processes at most one eligible email per run. It does not enable background inbox polling.
5. Check the new category label and log row. Execute again: an already-labelled message should be excluded. If nothing is eligible, execution ends without calling OpenAI.
6. Test one email per category plus an ambiguous message. Only add a Schedule Trigger after resolving the concurrency and partial-failure considerations below.

## Sheet columns, in order

```text
message_id,category,reason,needs_review
```

Sheets append uses RAW input, so text is not executed as spreadsheet formulas. No write node retries automatically.

## Configuration

### Configure workflow

Set spreadsheetId and sheetName. Replace all four categoryLabelIds with actual Gmail label IDs (Label_...), not display names. Keep the starter search query for your first run.

### Find one test email / Read Gmail message / Apply category label

Select the same Gmail OAuth2 credential on all three HTTP Request nodes.

### Classify with OpenAI

Select the OpenAI Header Auth credential. Keep the four allowed categories unless you update validation and label mapping too.

### Append review row

Select your Google Sheets OAuth2 credential.

## Example input

```json
{
  "subject": "Help with my invoice",
  "text": "Could you send a copy of invoice INV-1042? I cannot find it in the portal."
}
```

## Illustrative output (model output varies)

```json
{
  "message_id": "example-gmail-id",
  "category": "billing",
  "reason": "The sender asks for a copy of an invoice.",
  "needs_review": true
}
```

## Troubleshooting

### No messages returned

Confirm the test label is applied and no AHQ category label is already present. Empty results stop cleanly.

### No readable plain-text body

HTML-only emails and attachment-only messages are rejected. Use a text/plain MIME part or add a reviewed HTML-to-text conversion.

### Label applied but sheet failed

The next run excludes the labelled message. Recover the classification from the execution and repair the log; do not remove the label and rerun without checking.

### 403 from Gmail

Check Gmail API enablement, gmail.modify consent and that each Gmail node uses the correct credential.

Failed runs remain visible in n8n Executions; configure a separate error workflow for alerts. Webhook failures return an error rather than a success result. Check your caller's timeout; a timeout does not prove that no external write happened.

## Limitations

- A manual run processes one email. This is deliberately scoped inbox triage, not a background email agent.
- It does not read attachments, send replies, archive mail or remove existing labels. HTML-only bodies are not supported.
- An AI label can be wrong. The log marks every classification for review; no category triggers a business action.

## From demo to production

- Persist message_id and processing status in a durable store before enabling multiple workers or scheduled execution. Search exclusions alone do not prevent concurrent runs from selecting the same message.
- Treat Gmail labelling and Sheets logging as separate steps with recoverable state. Record partial completion and reconcile failed log writes.
- Evaluate categories on representative mail. Route ambiguous messages to a person instead of making commitments to customers.
- Use a dedicated mailbox, least-privilege access and explicit retention rules. Monitor volume, retry rates and changes in category distribution.

## Security and retention

The subject and up to 12,000 characters of plain-text body are sent to OpenAI. The log stores a message ID and explanation, not the body, but explanations can still contain sensitive details. n8n execution history can contain the original email. Restrict access and review retention before using real mail. Successful production execution payloads are not saved by this export; manual and failed executions are saved for debugging. Set pruning and binary-data retention explicitly, and delete synthetic test runs when finished. Header credentials may appear in webhook execution inputs; restrict access and rotate test keys.

## Costs

One Gmail search per run. A matching message adds one Gmail read, one model request, one label change and one Sheets append. Model retries can increase cost. Empty searches make no model call. Estimate per 1,000 runs as (input tokens × input price per million + output tokens × output price per million) / 1,000, then add hosting and retries. Use current pricing at https://openai.com/api/pricing/ and https://n8n.io/pricing/.

## Customization

- Add a scheduled trigger after implementing durable deduplication.
- Use a human-reviewed draft step for support responses.
- Replace the four categories with a tested, documented team taxonomy.

## Live acceptance checklist

- Record n8n version and import the JSON; confirm every node and credential type resolves.
- Configure only sandbox accounts and synthetic data.
- Run a valid case; compare each external write to the validated result.
- Run invalid input and simulated model refusal; confirm no downstream writes.
- Simulate provider failure and inspect partial completion.
- Check duplicate/retry behavior and delete test data.
- Record results before submitting to n8n or claiming live compatibility.

Need this connected to your actual systems? https://automatehq.org/#request-demo
