SpiderMail

Triage & Label an Inbox

This cookbook builds an inbox triage agent: a script that classifies every unread message, labels it, and files it — starring hot leads, snoozing things to follow up later, and bulk-archiving noise. It's the "decide before you act" companion to the auto-reply agent.

What you'll build

For each unread message, the agent:

  1. Classifies it (lead / support / noise) with an LLM.

  2. Applies a label and stars hot leads.

  3. Snoozes "follow up later" mail to resurface on a schedule.

  4. Bulk-archives everything classified as noise in one call.

Prerequisites

curl -X POST "https://spideriq.ai/api/v1/mail/labels" \
  -H "Authorization: Bearer $SPIDERIQ_TOKEN" -H "Content-Type: application/json" \
  -d '{"name":"lead","color":"#E11D48"}'

This cookbook classifies with SpiderGate, SpiderIQ's OpenAI-compatible LLM gateway, so you stay on one platform and one token. Any LLM works — swap the classify() body for your provider of choice.

The script

import os, json, requests

BASE = "https://spideriq.ai/api/v1"
TOKEN = os.environ["SPIDERIQ_TOKEN"]
HEAD = {"Authorization": f"Bearer {TOKEN}"}
MAILBOX = os.environ["MAILBOX"]

def get(path, **params):
    return requests.get(f"{BASE}{path}", headers=HEAD, params=params).json()
def post(path, body):
    return requests.post(f"{BASE}{path}", headers=HEAD, json=body).json()
def patch(path, body):
    return requests.patch(f"{BASE}{path}", headers=HEAD, json=body).json()

def classify(subject, body):
    # SpiderGate chat completion — returns one of: lead | support | noise
    r = requests.post("https://spideriq.ai/api/gate/v1/chat/completions",
        headers=HEAD, json={
            "model": "spideriq/classification",
            "messages": [{"role": "user", "content":
                f"Classify this email as exactly one word — lead, support, or noise.\n\n"
                f"Subject: {subject}\n\n{body}"}],
        }).json()
    return r["choices"][0]["message"]["content"].strip().lower()

inbox = get("/mail/inbox", email=MAILBOX, unread_only="true", limit=50)
to_archive = []

for msg in inbox.get("messages", []):
    mid = msg["id"]
    full = get(f"/mail/messages/{mid}")
    kind = classify(full.get("subject", ""), full.get("body_text", ""))

    if kind == "lead":
        patch(f"/mail/messages/{mid}", {"labels": ["lead"], "is_starred": True,
                                        "notes": "Auto-triaged: hot lead"})
    elif kind == "support":
        patch(f"/mail/messages/{mid}", {"labels": ["support"]})
        post(f"/mail/messages/{mid}/snooze", {"snoozed_until": "2026-06-09T09:00:00Z"})
    else:  # noise
        to_archive.append(mid)

# 4. One bulk call archives all the noise
if to_archive:
    post("/mail/messages/bulk", {"message_ids": to_archive, "action": "archive"})

print(f"triaged {len(inbox.get('messages', []))}, archived {len(to_archive)}")

Run it

python triage.py
# triaged 23, archived 9

Open the dashboard inbox and the leads are starred and labeled, support is filed and snoozed for Monday, and the noise is gone — all in one pass.

Make it sharper

  • Search instead of scanning all unread. Use /mail/search to triage only mail matching a query or sender.

  • Persist the rules as a view. Save a view like label:lead + starred so the team sees the triaged result instantly.

  • Hand off to the reply agent. Pipe the lead-labeled messages into the auto-reply agent for a first-touch response.

Tip: Bulk actions take up to 100 message ids per call — collect ids in a loop and archive/label them in one request instead of one PATCH per message. It's the fastest way for an agent to clear a backlog.

Next steps

  1. Auto-Reply Agent — respond to the leads you just found.

  2. Send Personalized Email — reach out at scale, one-to-one.

  3. Labels, Snooze & Views reference.