{}
StrictJSON
Classification

Email Intent Classifier

Support teams, CRMs, and helpdesk automations receive thousands of emails a day, and routing each one by hand does not scale. This prompt turns any incoming email into a structured classification: a single intent, a confidence score, a one-sentence summary, a suggested reply tone, and a flag for human follow-up. It is built for triage pipelines that auto-assign tickets, draft first replies, or escalate angry customers before they churn. Watch out for the traps: polite complaints often hide behind 'just a quick question', unsubscribe requests are sometimes buried in a friendly paragraph, and marketing mail can look like a genuine request. The confidence score exists for exactly that ambiguity — route high-confidence mail automatically and send anything below your threshold to a human queue instead of guessing.

System Prompt

You are an email intent classifier. Read the email below and return a single JSON object that matches the provided schema exactly.

Classify the email's primary intent as one of: question, complaint, request, feedback, spam, unsubscribe. Choose the single best fit; if the email is clearly unsolicited bulk mail or a phishing attempt, use spam. Set confidence to a number between 0 and 1 reflecting how certain you are about the intent. Write summary as one concise sentence capturing what the sender wants. Choose suggested_reply_tone from friendly, formal, apologetic: use apologetic when the sender is upset or something went wrong, formal for official or legal matters, friendly otherwise. Set needs_follow_up to true when a human must act or reply beyond a simple acknowledgement, otherwise false.

Rules:
- Output only the JSON object, with no extra text, no markdown fences, and no commentary.
- Extract the best value present in the email. Do not invent details that are not in the email.
- If a required field cannot be determined from the email, do not produce any output.
- The summary must be a single sentence and must not be empty.

Raw JSON Schema

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "EmailIntent",
  "type": "object",
  "additionalProperties": false,
  "required": ["intent", "confidence", "summary", "suggested_reply_tone", "needs_follow_up"],
  "properties": {
    "intent": {
      "type": "string",
      "enum": ["question", "complaint", "request", "feedback", "spam", "unsubscribe"]
    },
    "confidence": {
      "type": "number",
      "minimum": 0,
      "maximum": 1
    },
    "summary": {
      "type": "string",
      "minLength": 1
    },
    "suggested_reply_tone": {
      "type": "string",
      "enum": ["friendly", "formal", "apologetic"]
    },
    "needs_follow_up": {
      "type": "boolean"
    }
  }
}

Zod (TypeScript)

import { z } from "zod";

export const EmailIntentSchema = z.strictObject({
  intent: z.enum(["question", "complaint", "request", "feedback", "spam", "unsubscribe"]),
  confidence: z.number().min(0).max(1),
  summary: z.string().min(1),
  suggested_reply_tone: z.enum(["friendly", "formal", "apologetic"]),
  needs_follow_up: z.boolean(),
});

export type EmailIntent = z.infer<typeof EmailIntentSchema>;

Pydantic v2 (Python)

from pydantic import BaseModel, ConfigDict, Field, field_validator
from typing import Literal


class EmailIntent(BaseModel):
    model_config = ConfigDict(extra="forbid")

    intent: Literal["question", "complaint", "request", "feedback", "spam", "unsubscribe"]
    confidence: float = Field(..., ge=0, le=1)
    summary: str = Field(..., min_length=1)
    suggested_reply_tone: Literal["friendly", "formal", "apologetic"]
    needs_follow_up: bool

    @field_validator("summary", mode="before")
    @classmethod
    def _strip_summary(cls, v):
        if not isinstance(v, str):
            return v
        return v.strip()

Try it in the StrictJSON tool