Examples
Inbox classifier
Sort every unread message into a category with Claude, then apply labels for downstream queries
A Buyer agent that reads unread mail, asks Claude to pick a category, and applies labels per category. Once labeled, you can query the inbox by category — labels: "sales", labels: "high-priority" — without re-running the classifier.
Before you start
- A project at console.loomal.ai with Mail turned on.
- Your Loomal API key (
loid-...) and an Anthropic API key (sk-ant-...).
npm install @loomal/sdk @anthropic-ai/sdkThe classifier
The label scheme below is just one example. Labels are free-form on Loomal — pick a vocabulary that matches your team's pipeline.
| Classification | Labels applied |
|---|---|
sales | sales, high-priority |
support | support |
meeting | meeting |
newsletter | newsletter, low-priority |
spam | spam, low-priority |
import { Loomal } from "@loomal/sdk";
import Anthropic from "@anthropic-ai/sdk";
const loomal = new Loomal({ apiKey: process.env.LOOMAL_API_KEY! });
const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY! });
const CATEGORIES = ["sales", "support", "meeting", "newsletter", "spam"] as const;
type Category = (typeof CATEGORIES)[number];
async function classify(subject: string, body: string): Promise<Category> {
const completion = await anthropic.messages.create({
model: "claude-haiku-4-5-20251001",
max_tokens: 16,
system: `Classify the email into exactly one of: ${CATEGORIES.join(", ")}. Reply with the single word only.`,
messages: [
{ role: "user", content: `Subject: ${subject}\n\n${body}` },
],
});
const text = completion.content
.filter((b): b is Anthropic.TextBlock => b.type === "text")
.map((b) => b.text)
.join("")
.trim()
.toLowerCase();
return (CATEGORIES.includes(text as Category) ? text : "support") as Category;
}
function labelsFor(category: Category): string[] {
if (category === "sales") return ["sales", "high-priority"];
if (category === "newsletter") return ["newsletter", "low-priority"];
if (category === "spam") return ["spam", "low-priority"];
return [category];
}
async function run() {
const { messages } = await loomal.mail.listMessages({
limit: 50,
labels: "unread",
});
for (const m of messages) {
const category = await classify(m.subject, m.extractedText || m.text);
await loomal.mail.updateLabels(m.messageId, {
addLabels: ["read", "classified", ...labelsFor(category)],
removeLabels: ["unread"],
});
console.log(`${m.from[0]} -> ${category}`);
}
}
run();Run with LOOMAL_API_KEY=loid-... ANTHROPIC_API_KEY=sk-ant-... npx tsx classifier.ts.
Query the results
Once classified, pull each category with a single call:
const sales = await loomal.mail.listMessages({ labels: "sales", limit: 50 });
const urgent = await loomal.mail.listMessages({ labels: "high-priority", limit: 50 });