> ## Documentation Index
> Fetch the complete documentation index at: https://docs.withboundary.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Email Parsing

> Validate parsed email intent, priority, department, and summary before creating tickets.

## The problem

Your email parser returns this:

```json theme={null}
{
  "sender": "jane@acme.com",
  "subject": "URGENT: Server down in production",
  "intent": "urgent_support",
  "priority": "low",
  "department": "sales",
  "summary": "Server issue"
}
```

The JSON is valid. The values are not. An urgent support email should not be low priority, routed to sales, or summarized so briefly that the receiving team has no context.

## The contract

```typescript theme={null}
import { z } from "zod";
import { defineContract } from "@withboundary/contract";

export const emailSchema = z.object({
  sender: z.string().email(),
  subject: z.string(),
  intent: z.enum([
    "general_inquiry",
    "billing",
    "urgent_support",
    "feature_request",
    "cancellation",
  ]),
  priority: z.enum(["low", "medium", "high", "critical"]),
  department: z.enum(["sales", "support", "engineering", "billing", "product"]),
  summary: z.string(),
});

export const emailContract = defineContract({
  name: "email-parsing",
  schema: emailSchema,
  rules: [
    {
      name: "urgent_requires_high_priority",
      description: "Urgent intents must be high or critical priority",
      fields: ["intent", "priority"],
      check: (email) =>
        !email.intent.includes("urgent") || ["high", "critical"].includes(email.priority)
          || `intent is "${email.intent}" but priority is "${email.priority}"; use high or critical`,
    },
    {
      name: "support_routed_to_support",
      description: "Support intents route to support or engineering",
      fields: ["intent", "department"],
      check: (email) =>
        !email.intent.includes("support") || ["support", "engineering"].includes(email.department)
          || `support intent routed to "${email.department}"; use support or engineering`,
    },
    {
      name: "cancellation_routed_correctly",
      description: "Cancellation requests route to billing or support",
      fields: ["intent", "department"],
      check: (email) =>
        email.intent !== "cancellation" || ["billing", "support"].includes(email.department)
          || `cancellation routed to "${email.department}"; use billing or support`,
    },
    {
      name: "summary_substantive",
      description: "Summary has enough context for the receiving team",
      fields: ["summary"],
      check: (email) =>
        email.summary.trim().length >= 20
          || "summary must include enough context for the receiving team",
    },
  ],
});
```

## Run the model

```typescript theme={null}
const result = await emailContract.accept(async (attempt) => {
  const response = await callYourLLM({
    messages: [
      {
        role: "user",
        content: [
          "Parse this email as JSON.",
          attempt.instructions,
          `From: ${email.from}`,
          `Subject: ${email.subject}`,
          `Body: ${email.body}`,
        ].join("\n\n"),
      },
      ...attempt.repairs,
    ],
  });

  return response.text;
});
```

## Accept or reject

```typescript theme={null}
if (result.ok) {
  await ticketSystem.create({
    from: result.data.sender,
    priority: result.data.priority,
    department: result.data.department,
    summary: result.data.summary,
  });

  return { status: "created" };
}

logger.warn("email parse rejected", {
  emailId,
  attempts: result.error.attempts.length,
  lastFailure: result.error.attempts.at(-1),
});

await triageQueue.add({
  emailId,
  reason: result.error.message,
  attempts: result.error.attempts,
});

return { status: "needs_triage" };
```

Rejected parses can be reviewed, retried with more source context, or dropped according to your support workflow. They should not create tickets with guessed routing.

## When to use this pattern

* Inbound email triage and routing
* Customer support ticket classification
* Email-to-CRM extraction
* Invoice or contract emails with structured attachments
* Digest generation where routing or priority matters
